/* Decimal signed fixed point arithmetic implementation in C++. METHADONE.CPP version 0.26, februari 4, 2007. Latest version available at: http://kmt.hku.nl/~pieter/SOFT/ Copyright (c) 2006 2007 Pieter Suurmond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is requested to send the modifications to the original developer so that they can be incorporated into the canonical version. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include // Prior to "fixd.hpp". #include "int.h" // Prior to "fixd.hpp". #include "fixd.hpp" #include "bcd_arithmetic.hpp" // Internal low level routines. using namespace std; /* ----------------------------------------------------------------------------- CONSTRUCTORS: */ fixd::fixd() // Calling the default constructor is inhibited: { throw "fixd's default constructor may not be called,\ the number of decimal digits must always be specified.\n"; } fixd::fixd(S32 digits) // Constructor. { if (digits > 1000000) throw "One million is the maximum number of digits in an fixd-object!\n"; if ((digits & 1) || (digits < 2)) // Guarantee num >= 2. throw "The number of digits must be a positive multiple of 2!\n"; this->num = digits; this->data = new U8[this->num]; fixd_reset(this->num, this->data); // Initialise to zero. } fixd::fixd(const fixd& x) // Copy constructor. { this->num = x.num; this->data = new U8[this->num]; fixd_copy(this->num, this->data, x.data); } /* ----------------------------------------------------------------------------- DESTRUCTOR: */ fixd::~fixd() { delete [] this->data; } /* ----------------------------------------------------------------------------- READ-ONLY INFO: */ S32 fixd::digits() const // (Not used within this file itself.) { return (this->num); } #if (0) /* ----------------------------------------------------------------------------- ASSIGNMENT OPERATORS: */ fixd& fixd::operator = (const fixd& x) // Assignment operator. { // Sign extension when going to a larger size. // Inhibit truncation/overflow/loss of data when going to a smaller size. if (this->num != x.num) // For now, sizes must be the same. throw "Assignment to different size is not allowed (for now).\n"; fixd_copy(this->num, this->data, x.data); // See fxp_core.hpp. return *this; } // This assignment requires an explicit inhibition. Otherwise, C++ will call // constructor fxp(S32) automatically! fixd& fxp::operator = (const double& x) { (void)x; // Surpress compiler warning "x was never referenced". throw "Floats and doubles may not be assigned (=) to fxp objects.\n"; } fixd& fixd::operator = (S32 i) { fixd_set32(this->num, this->data, i); return *this; } /* TODO: fixd& fixd::operator = (S64 ii) { if (this->num < 4) throw "Fxp too small for 64 bit assignment (=).\n"; fixd_set64(this->num, this->data, ii); } */ /* ----------------------------------------------------------------------------- ARITHMETIC OPERATORS: */ fixd fixd::operator + (const fixd& y) const { if (this->num != y.num) throw "Can only add (+) fixd's with the same size (for now)!\n"; fixd result(*this); // Copy-constructor. S8 e = fixd_add(result.num, result.data, y.data); // In-place. if (e) { if (e > 0) throw "fixd + operator overflow!\n"; throw "fixd + operator underflow!\n"; } return result; // Return dynamically created object. } fixd fixd::operator - (const fixd& y) const // (Binary minus operator.) { if (this->num != y.num) throw "Can only subtract (-) fixd's with the same size!\n"; fixd result(*this); S8 e = fixd_sub(result.num, result.data, y.data); if (e) { if (e > 0) throw "fixd - operator overflow!\n"; throw "fixd - operator underflow!\n"; } return result; // Return dynamically created object. } fixd fixd::operator * (const fixd& y) const { if (this->num != y.num) throw "Can only multiply (*) fixd's with the same size!\n"; fixd result(*this); S8 e = fixd_mul(result.num, result.data, y.data); if (e) { if (e > 0) throw "fixd * operator overflow!\n"; throw "fixd * operator underflow!\n"; } return result; // Return dynamically created object. } fixd fixd::operator / (const fixd& y) const { if (this->num != y.num) throw "Can only divide (/) fixd's with the same size!\n"; fixd result(y.num << k2LOG_NUMBITS); // Same size as both arguments. S8 e = fixd_div(this->num, result.data, this->data, y.data); if (e) { if (e > 0) throw "fixd / operator overflow!\n"; throw "fixd / operator underflow!\n"; } return result; // Return dynamically created object. } fixd& fixd::operator += (const fixd& x) { if (this->num != x.num) throw "Can only add (+=) fixd's with the same size!\n"; S8 e = fixd_add(this->num, this->data, x.data); if (e) { if (e > 0) throw "fixd += operator overflow!\n"; throw "fixd += operator underflow!\n"; } return *this; } fixd& fixd::operator -= (const fixd& x) { if (this->num != x.num) throw "Can only subtract (-=) fixd's with the same size!\n"; S8 e = fixd_sub(this->num, this->data, x.data); if (e) { if (e > 0) throw "fixd -= operator overflow!\n"; throw "fixd -= operator underflow!\n"; } return *this; } fixd& fixd::operator *= (const fixd& x) { if (this->num != x.num) throw "Can only multiply (*=) fixd's with the same size!\n"; S8 e = fixd_mul(this->num, this->data, x.data); if (e) { if (e > 0) throw "fixd *= operator overflow!\n"; throw "fixd *= operator underflow!\n"; } return *this; } fixd& fixd::operator /= (const fixd& x) { if (this->num != x.num) throw "Can only divide (/=) fixd's with the same size!\n"; S8 e = fixd_div(this->num, this->data, this->data, x.data); if (e) { if (e > 0) throw "fixd /= operator overflow!\n"; throw "fixd /= operator underflow!\n"; } return *this; } fixd fixd::operator - () const // Negation (unary minus operator). { fxp result(*this); // Same size as argument. if (fixd_neg(result.num, result.data)) // Underflow is impossible. throw "fixd unary - operator (negation) overflow!\n"; return result; // Return dynamically created object. } bool fixd::operator == (const fixd& y) const { if (this->num != y.num) throw "Can only compare (==) fixd's with the same size!\n"; return fixd_eq(this->num, this->data, y.data); // Sure num is even! } bool fixd::operator != (const fixd& y) const { if (this->num != y.num) throw "Can only compare (!=) fixd's with the same size!\n"; return (!fixd_eq(this->num, this->data, y.data)); // Sure num is even! } bool fixd::operator < (const fixd& y) const { if (this->num != y.num) throw "Can only compare (<) fixd's with the same size!\n"; return fixd_lt(this->num, this->data, y.data); } bool fixd::operator > (const fixd& y) const { if (this->num != y.num) throw "Can only compare (>) fixd's with the same size!\n"; return fixd_gt(this->num, this->data, y.data); } bool fixd::operator <= (const fixd& y) const { if (this->num != y.num) throw "Can only compare (<=) fixd's with the same size!\n"; return fixd_lte(this->num, this->data, y.data); } bool fixd::operator >= (const fixd& y) const { if (this->num != y.num) throw "Can only compare (>=) fixd's with the same size!\n"; return fixd_gte(this->num, this->data, y.data); } // Conversion to bool operator: fixd::operator bool() const { return fixd_nonzero(this->num, this->data); } /* ----------------------------------------------------------------------------- ARITHMETIC MEMBER FUNCTIONS: */ fixd fixd::abs() const // fixd member function for syntax: x.abs(). { fixd result(*this); // Copy constructor. if (fixd_abs(result.num, result.data)) throw "fxp abs() overflow!\n"; return result; // Return dynamically created object. } fixd abs(const fixd& x) // fixd 'friend' function for syntax: abs(x). { fixd result(x); // Copy constructor. if (fxp_abs(result.num, result.data)) throw "fxp abs(const fxp&) overflow!\n"; return result; // Return dynamically created object. } /* ----------------------------------------------------------------------------- I/O: */ #include // Input stream operator >> needs strchr(). #include // For isspace() and such. ostream& operator << (ostream& os, const fixd& x) { if (os.flags() & ios_base::hex) // Ignore other flags. { // Save flags before altering them: ios_base::fmtflags f = os.flags(); // Force leading zero's to be printed: os.setf(ios_base::right, ios_base::adjustfield); os.fill('0'); const tBBB *r = x.data, *e = x.data + (x.num >> 1); // Sure num >= 2 and even. do { // Width() needs to be repeated. os.width(kNUMBITS/4); // One hex-character per nibble. os << #if (kNUMBITS==8) (tBBBBBB) // U16-typecast is necessary for 8 bit tBBB's. #endif // Otherwise, characters instead of numbers are printed. (*r++); } while (r < e); os << '.'; e = x.data + x.num; do { os.width(kNUMBITS/4); os << #if (kNUMBITS==8) (tBBBBBB) // U16-typecast is necessary for 8 bit tBBB's. #endif // Otherwise, characters instead of numbers are printed. (*r++); } while (r < e); // Restore flags: os.flags(f); } else if (os.flags() & ios_base::dec) // Ignore other flags. { char* s = fxp_new_dec_str(x.num, x.data, (U32)os.precision()); os << s; // Take the floating point precision. delete [] s; } else { throw "Only decimal and hexadecimal output is supported.\n"; } return os; } istream& operator >> (istream& is, fixd& x) { char c; if (is.flags() & ios_base::skipws) // Eat away leading whitespace { // only if that is selected. while (is.get(c)) { if (!isspace(c)) // See . { is.putback(c); break; } } } // Always eat away leading zeros: while (is.get(c)) { if (c != '0') { is.putback(c); break; } } fixd_reset(x.num, x.data); U32 count = 1; // NOTE: max(U32) digits max! U32 dot = 0; U8 eat = 1; if (is.flags() & ios_base::hex) // Ignore other flags. { U32 hexmax = x.num << (k2LOG_NUMBITS - 3); // Max digits before and // after the dot. // (8 nibbles per 32 bits.) while (is.get(c)) { static const char* hextable = "0123456789abcdefABCDEF"; char* t = strchr(hextable, c); // Make sure c is not null! if (t) // Or use 'isxdigit(c)'. { U32 nibble = (t - hextable); if (nibble > 15) nibble -= 6; // a -> A. count += eat; // Do not count remaining zero's. if (count - dot - 1 > hexmax) { if (dot) // After the dot. { eat = 0; // Ignore all trailing hex digits. // Correct bipolar rounding. // if (nibble > 7) // fxp_inc(x); } else // Before the dot. throw "Too many hexadecimal digits for input!\n"; } if (eat) { fxp_asl(x.num, x.data, 4); // Shift left 4 bits, no throw. x.data[x.num-1] |= nibble; // Insert least significant nibble. } } else if ((c == '.') && (!dot)) // Or use ispunct()? { dot = count; } else { is.putback(c); break; // Ok, done. } } // Shift in trailing zero's if necessary. if (!dot) dot = count; fxp_asl(x.num, x.data, (hexmax - count + dot) << 2); } else if (is.flags() & ios_base::dec) // Ignore other flags. { fixd d(x.num << k2LOG_NUMBITS); fixd ten(x.num << k2LOG_NUMBITS); ten.data[(ten.num >> 1) - 1] = 10; while (is.get(c)) { static const char* dectable = "0123456789"; char* t = strchr(dectable, c); // Make sure c is not null! if (t) // Or use 'isxdigit(c)'. { U32 decimal = (t - dectable); count += eat; if (eat) { d.data[x.num - 1] = decimal; // Can we use unsigned arithmetic here?! if (fxp_mul(x.num, x.data, ten.data) | // *10. fxp_add(x.num, x.data, d.data)) // +d. throw "Too many decimal digits for 'fxp' input!\n"; } } else if ((c == '.') && (!dot)) // Or use ispunct()? { dot = count; } else { is.putback(c); break; // Ok, done. } } // Divide by 10^(count - dot). } else { throw "Only decimal and hexadecimal input is supported.\n"; } return is; } #endif