/** @file fixed.hpp */ /** Listing 49-5. Changing fixed from a Class to a Class Template */ #ifndef FIXED_HPP_ #define FIXED_HPP_ #include #include #include #include #include #include #include #include #include #include #include "ioflags.hpp" #include "power10.hpp" /** @brief Implement a fixed-point number class template. * Values have @c N places after the decimal point. * All arithmetic follows the usual rules. */ template class fixed { public: typedef T value_type; ///< Type of the actual value static value_type const places = N; ///< number of decimal places static value_type const places10 = power10::value; ///< 10places /// Default constructor initializes to zero. fixed() : value_() {} /// Construct from separate integer and fractional parts, /// e.g., initialize to 123.45 with fixed(123, 45). Initialize /// to 12.07 with fixed(12, 7). /// @param integer The integer part of the initial value /// @param fraction The fraction part of the initial value fixed(value_type integer, value_type fraction); /// Construct from an integer with no fractional part. /// @param integer The initial value fixed(value_type integer); /// Construct by rounding off a floating point number. /// @param value The initial value fixed(double value) : value_(static_cast(value * places10 + (value < 0 ? -0.5 : 0.5))) {} /// Convert to a string. /// @returns a string representation of the value, e.g., "123.04" std::string as_string() const; /// Read from a stream. /// Overwrite this value with the value read from the stream. /// @param strm the stream to read /// @returns true for success or false for failure template bool read(std::basic_istream& strm); /// Convert to long double. double as_long_double() const { return static_cast(value()) / places10; } /// Convert to double. double as_double() const { return static_cast(value()) / places10; } /// Convert to float float as_float() const { return static_cast(value()) / places10; } /// Return just the integer part, rounded off to the nearest integer. /// If the value lies equidistant between two integers, round even /// numbers up and odd numbers down (banker's rounding). value_type round() const; /// Return the integer part (which is the same as trunc()). value_type integer() const { return value() / places10; } /// Return the fractional part, e.g., 3 for 12.03 value_type fraction() const; /// Addition assignment operator /// @param f The right-hand operand fixed& operator+=(fixed f); /// Subtraction assignment operator /// @param f The right-hand operand fixed& operator-=(fixed f); /// Multiplication assignment operator /// @param f The right-hand operand fixed& operator*=(fixed f); /// Multiplication assignment operator /// @param v The right-hand operand fixed& operator*=(value_type v); /// Division assignment operator /// @param f The right-hand operand fixed& operator/=(fixed f); /// Division assignment operator /// @param v The right-hand operand fixed& operator/=(value_type v); /// Negate this value. void negate(); /// Pre-increment fixed& operator++(); /// Post-increment fixed operator++(int); /// Pre-decrement fixed& operator--(); /// Post-decrement fixed operator--(int); /// Return the internal value. value_type value() const { return value_; } private: /// Reduce frac to the range [0, places10) by discarding digits to the right. value_type reduce(value_type frac); value_type value_; }; // Construct a fixed value from an integer part and a fraction part template fixed::fixed(value_type integer, value_type fraction) { if (fraction < T()) throw std::invalid_argument("negative fraction not allowed"); fraction = reduce(fraction); if (integer < T()) value_ = integer * places10 - fraction; else value_ = integer * places10 + fraction; } // Construct a fixed value from an integer part with no fraction template fixed::fixed(value_type integer) : value_(integer * places10) {} // Get the fraction part template typename fixed::value_type fixed::fraction() const { return std::abs(value()) % places10; } /// Reduce the fractional part to the range [0, places10). /// Imagine frac has the format F(G(XY*)?)?. /// The resulting value is FH, where H == 0 if G is absent, /// or H == G+1 if X==5 and Y* == 0* and G is odd, or /// H == G+1 if X>5 or X==5 and Y*>0*, else H == G. /// In other words, check that frac ends with only zero digits, /// then a 5, then two more digits (searching from least-significant /// to most-significant). If so, implement banker's rounding. /// Otherwise, round GXY* to the nearest value (G+1 or G). template typename fixed::value_type fixed::reduce(value_type frac) { // First scan for zero digits on the right. value_type f(frac); while (f >= places10*10 and f % 10 == 0) { f /= 10; } if (f >= places10*10) { int x(0); // Loop ended because a non-zero digit was seen so Y* > 0. // Discard the remaining digits, but keep track of the last // digit to be processed (X). while (f >= places10) { x = f % 10; f /= 10; } // Round up if the last digit (X) is 5 or more if (x >= 5) ++f; return f; } // Else all digits so far are zero. Check how many digits there were, // that is, check whether G, and X at least are present. else if (f >= places10) { // Yes, G and X are present. If X == 5, implement banker's rounding. // Otherwise, round to nearest. int x(f % 10); f /= 10; assert(f < places10); if (x == 5) { // Yes, so implement banker's rounding. if (f % 2 != 0) ++f; return f; } else if (x < 5) { // Round down. return f; } else { // Round up. return f + 1; } } // Not enough digits, so nothing to round. assert(frac < places10); return frac; } // Round off to nearest integer. template typename fixed::value_type fixed::round() const { const value_type frac(fraction()); int adjust(value() < 0 ? -1 : +1); if (frac > places10/2) return integer()+adjust; else if (frac < places10/2) return integer(); else if (integer() % 2 == 0) return integer(); else return integer()+adjust; } // Convert to a string using fixed-point notation. template std::string fixed::as_string() const { std::ostringstream out; out << integer() << '.' << std::setfill('0') << std::setw(places) << fraction(); return out.str(); } template fixed& fixed::operator+=(fixed f) { value_ += f.value(); return *this; } template fixed& fixed::operator-=(fixed f) { value_ -= f.value(); return *this; } template fixed& fixed::operator*=(fixed f) { value_ = (value_ * f.value()) / places10; return *this; } template fixed& fixed::operator*=(value_type v) { value_ *= v; return *this; } template fixed& fixed::operator/=(fixed f) { value_ = (value_ * places10) / f.value(); return *this; } template fixed& fixed::operator/=(value_type v) { value_ /= v; return *this; } template void fixed::negate() { value_ = -value_; } template fixed& fixed::operator++() { value_ += places10; return *this; } template fixed fixed::operator++(int) { fixed result(*this); ++*this; return result; } template fixed& fixed::operator--() { value_ -= places10; return *this; } template fixed fixed::operator--(int) { fixed result(*this); --*this; return result; } template template bool fixed::read(std::basic_istream& strm) { ioflags flags(strm); value_type integer; char decimal; if (not (strm >> integer)) return false; strm.unsetf(std::ios_base::skipws); if (not (strm >> decimal) or decimal != '.') { // Just an integer is fine. Push back the non-decimal character, // if there is one, and reset the stream flags to show that // reading the fixed value succeeded. strm.unget(); strm.clear(strm.rdstate() & ~strm.failbit); value_ = integer * places10; return true; } else { value_type fraction(0); char c; int p(0); // Read one extra place for round-off. for (; p != places+1 and strm >> c and std::isdigit(c, strm.getloc()); ++p) { fraction = fraction * 10 + (c - '0'); } // Pad out to the requisite number of decimal places. for (; p < places; ++p) fraction = fraction * 10; // If the loop terminated because the maximum number of decimal // places were read, keep reading the stream to discard excees digits. while (strm and std::isdigit(c, strm.getloc())) strm >> c; // Push back the last, non-digit character read from the stream. // If the stream reached EOF, unget() is harmless. strm.unget(); // Clear failbit because even if reading a character or whatever // failed, reading the fixed value did not. strm.clear(strm.rdstate() & ~strm.failbit); fraction = reduce(fraction); if (integer < 0) value_ = integer * places10 - fraction; else value_ = integer * places10 + fraction; } return true; } /// Read a fixed value /// @param strm The input stream /// @param f The result template std::basic_istream& operator>>(std::basic_istream& strm, fixed& f) { if (not f.read(strm)) strm.setstate(strm.failbit); return strm; } /// Write a fixed value /// @param strm The output stream /// @param f The value to write template std::basic_ostream& operator<<(std::basic_ostream& strm, fixed f) { strm << f.as_string(); return strm; } /// Add fixed values /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator+(fixed a, fixed b) { a += b; return a; } /// Add a fixed value and an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator+(fixed a, T b) { a += fixed(b); return a; } /// Add a fixed value and an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator+(T a, fixed b) { b += a; return b; } /// Subtract fixed values /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator-(fixed a, fixed b) { a -= b; return a; } /// Subtract an integer from a fixed value /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator-(fixed a, T b) { a -= fixed(b); return a; } /// Subtract a fixed value from an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator-(T a, fixed b) { b += -a; return -b; } /// Multiply fixed values /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator*(fixed a, fixed b) { a *= b; return a; } /// Multiply a fixed value and an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator*(fixed a, T b) { a *= b; return a; } /// Multiply a fixed value and an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator*(T a, fixed b) { b *= a; return b; } /// Divide fixed values /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator/(fixed a, fixed b) { a /= b; return a; } /// Divide an integer by a fixed value /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator/(fixed a, T b) { a /= b; return a; } /// Divide a fixed value by an integer /// @param a The left-hand operand /// @param b The right-hand operand template fixed operator/(T a, fixed b) { fixed tmp(a); tmp /= b; return tmp; } /// Negate a fixed value /// @param a The value to negate template fixed operator-(fixed a) { a.negate(); return a; } /// Compare fixed values for equality by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator==(fixed a, fixed b) { return a.value() == b.value(); } /// Compare a fixed value and an integer for equality /// @param a The left-hand operand /// @param b The right-hand operand template bool operator==(T a, fixed b) { return a == b.value(); } /// Compare a fixed value and an integer for equality /// @param a The left-hand operand /// @param b The right-hand operand template bool operator==(fixed a, T b) { return a.value() == b; } /// Compare fixed values for inequality by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator!=(fixed a, fixed b) { return not (a == b); } /// Compare a fixed value and an integer for inequality /// @param a The left-hand operand /// @param b The right-hand operand template bool operator!=(T a, fixed b) { return not (a == b); } /// Compare a fixed value and an integer for inequality /// @param a The left-hand operand /// @param b The right-hand operand template bool operator!=(fixed a, T b) { return not (a == b); } /// Compare fixed values for less-than by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<(fixed a, fixed b) { return a.value() < b.value(); } /// Compare a fixed value and an integer for less-than /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<(T a, fixed b) { return a < b.value(); } /// Compare a fixed value and an integer for less-than /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<(fixed a, T b) { return a.value() < b; } /// Compare fixed values for greater-than by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>(fixed a, fixed b) { return b < a; } /// Compare a fixed value and an integer for greater-than /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>(T a, fixed b) { return b < a; } /// Compare a fixed value and an integer for greater-than /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>(fixed a, T b) { return b < a; } /// Compare fixed values for less-than-or-equal by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<=(fixed a, fixed b) { return not (b < a); } /// Compare a fixed value and an integer for less-than-or-equal /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<=(T a, fixed b) { return not (b < a); } /// Compare a fixed value and an integer for less-than-or-equal /// @param a The left-hand operand /// @param b The right-hand operand template bool operator<=(fixed a, T b) { return not (b < a); } /// Compare fixed values for greater-than-or-equal by comparing the underlying values. /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>=(fixed a, fixed b) { return not (a < b); } /// Compare a fixed value and an integer for greater-than-or-equal /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>=(T a, fixed b) { return not (a < b); } /// Compare a fixed value and an integer for greater-than-or-equal /// @param a The left-hand operand /// @param b The right-hand operand template bool operator>=(fixed a, T b) { return not (a < b); } #endif