From a40e95192368a8d06b4466d11c1e0db14970d00c Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Wed, 22 Sep 2010 19:43:12 +0000 Subject: [PATCH] fixed: Big::ToDouble(double &) set always +INF (infinity) when the value was too large (even for negative values) (it should set -INF in such a case) added: some missing methods for converting for UInt<>, Int<> and Big<> classes: uint ToUInt() sint ToInt() ToUInt(uint32_t &) ToInt(uint32_t &) ToInt(int32_t &) ToUInt(uint64_t &) ToInt(uint64_t &) ToInt(int64_t &) FromUInt(uint32_t &) FromInt(uint32_t &) FromInt(int32_t &) FromUInt(uint64_t &) FromInt(uint64_t &) FromInt(int64_t &) and appropriate constructors and operators *** version for 64 bit platforms are not tested yet *** added: double Big::ToDouble() /there was only Big::ToDouble(double &) / uint Big::ToFloat(float &) float Big::ToFloat() git-svn-id: svn://ttmath.org/publicrep/ttmath/trunk@316 e52654a7-88a9-db11-a3e9-0013d4bc506e --- CHANGELOG | 25 +++- samples/big.cpp | 8 +- samples/big2.cpp | 8 +- samples/parser.cpp | 2 +- ttmath/ttmathbig.h | 358 +++++++++++++++++++++++++++++++++++++-------- ttmath/ttmathint.h | 86 ++++++++++- 6 files changed, 419 insertions(+), 68 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ff69082..7b8201e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Version 0.9.2 prerelease (2010.09.21): +Version 0.9.2 prerelease (2010.09.22): * fixed: Big::Add() sometimes incorrectly rounded the last bit from its mantissa * fixed: Big::BigAnd() Big::BigOr() Big::BigXor() should have set NaN when the argument was negative (they only returned 2) @@ -8,6 +8,9 @@ Version 0.9.2 prerelease (2010.09.21): in some cases when in the output string the exponent should be equal zero the method changes the exponent to one so the last digit from the mantissa was lost + * fixed: Big::ToDouble(double &) set always +INF (infinity) + when the value was too large (even for negative values) + (it should set -INF in such a case) * added: some missing operators UInt::operator~() /* bitwise neg */ UInt::operator&() /* bitwise and */ @@ -54,6 +57,26 @@ Version 0.9.2 prerelease (2010.09.21): but you can force using asm version (the same asm as for Microsoft Visual) by defining TTMATH_FORCEASM macro you have to be sure that your compiler accept such an asm format + * added: some missing methods for converting + for UInt<>, Int<> and Big<> classes: + uint ToUInt() + sint ToInt() + ToUInt(uint32_t &) + ToInt(uint32_t &) + ToInt(int32_t &) + ToUInt(uint64_t &) + ToInt(uint64_t &) + ToInt(int64_t &) + FromUInt(uint32_t &) + FromInt(uint32_t &) + FromInt(int32_t &) + FromUInt(uint64_t &) + FromInt(uint64_t &) + FromInt(int64_t &) + and appropriate constructors and operators + * added: double Big::ToDouble() /there was only Big::ToDouble(double &) / + uint Big::ToFloat(float &) + float Big::ToFloat() * changed: now asm version is available only on x86 and amd64 (and only for GCC and MS VC compilers) * removed: macro TTMATH_RELEASE diff --git a/samples/big.cpp b/samples/big.cpp index 8e9fa6a..fb17203 100644 --- a/samples/big.cpp +++ b/samples/big.cpp @@ -88,13 +88,13 @@ a = 123456.543456 b = 98767878.124322 a + b = 98891334.667778 a - b = -98644421.580866 -a * b = 12193540837712.2708 +a * b = 12193540837712.27076 a / b = 0.00124996654580957646 Calculating with a carry -a = 1.624801256066640878e+646457012 +a = 1.6248012560666408782e+646457012 b = 456.319999999999993 -a + b = 1.624801256066640878e+646457012 -a - b = 1.624801256066640878e+646457012 +a + b = 1.6248012560666408782e+646457012 +a - b = 1.6248012560666408782e+646457012 a * b = (carry) a / b = 3.560661939136222174e+646457009 */ diff --git a/samples/big2.cpp b/samples/big2.cpp index fcb74bf..9bd253b 100644 --- a/samples/big2.cpp +++ b/samples/big2.cpp @@ -102,12 +102,12 @@ b = 98767878.124322 a + b = 98891334.667778 a - b = -98644421.580866 a * b = 12193540837712.270763536832 -a / b = 0.001249966545809576460596448526166860913 +a / b = 0.0012499665458095764605964485261668609133 Calculating with a carry -a = 2.3495345545711177736883282090959505003e+2776511644261678604 +a = 2.34953455457111777368832820909595050034e+2776511644261678604 b = 456.3199999999999931787897367030382156 -a + b = 2.3495345545711177736883282090959505003e+2776511644261678604 -a - b = 2.3495345545711177736883282090959505003e+2776511644261678604 +a + b = 2.34953455457111777368832820909595050034e+2776511644261678604 +a - b = 2.34953455457111777368832820909595050034e+2776511644261678604 a * b = (carry) a / b = 5.1488748127873374141170361292780486452e+2776511644261678601 */ diff --git a/samples/parser.cpp b/samples/parser.cpp index 6cbf192..6ee2e02 100644 --- a/samples/parser.cpp +++ b/samples/parser.cpp @@ -29,7 +29,7 @@ const char equation[] = " (34 + 24) * 123 - 34.32 ^ 6 * sin(2.56) - atan(10)"; /* the result (on 32 bit platform): --897705014.52573107 +-897705014.525731067 */ diff --git a/ttmath/ttmathbig.h b/ttmath/ttmathbig.h index 809f6cf..b51d42b 100644 --- a/ttmath/ttmathbig.h +++ b/ttmath/ttmathbig.h @@ -2260,22 +2260,19 @@ public: } - /*! - this method converts 'this' into 'result' +private: + /*! + an auxiliary method for converting 'this' into 'result' if the value is too big this method returns a carry (1) */ - uint ToUInt(uint & result, bool test_sign = true) const + uint ToUIntOrInt(uint & result) const { result = 0; if( IsZero() ) return 0; - if( test_sign && IsSign() ) - // the result should be positive - return 1; - sint maxbit = -sint(man*TTMATH_BITS_PER_UINT); if( exponent > maxbit + sint(TTMATH_BITS_PER_UINT) ) @@ -2287,21 +2284,74 @@ public: // our value is from the range of (-1,1) and we return zero return 0; - UInt mantissa_temp(mantissa); // exponent is from a range of (maxbit, maxbit + sint(TTMATH_BITS_PER_UINT) > + // and [maxbit + sint(TTMATH_BITS_PER_UINT] <= 0 sint how_many_bits = exponent.ToInt(); // how_many_bits is negative, we'll make it positive how_many_bits = -how_many_bits; - // we're taking into account only the last word in a mantissa table - mantissa_temp.Rcr( how_many_bits % TTMATH_BITS_PER_UINT, 0 ); - result = mantissa_temp.table[ man-1 ]; + result = (mantissa.table[man-1] >> (how_many_bits % TTMATH_BITS_PER_UINT)); return 0; } +public: + + /*! + this method converts 'this' into uint + */ + uint ToUInt() const + { + uint result; + + ToUInt(result); + + return result; + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + uint ToUInt(uint & result) const + { + if( ToUIntOrInt(result) ) + return 1; + + if( IsSign() ) + return 1; + + return 0; + } + + + /*! + this method converts 'this' into sint + */ + sint ToInt() const + { + sint result; + + ToInt(result); + + return result; + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + uint ToInt(uint & result) const + { + return ToUInt(result); + } + /*! this method converts 'this' into 'result' @@ -2310,36 +2360,35 @@ public: */ uint ToInt(sint & result) const { - result = 0; - uint result_uint; + uint result_uint; - if( ToUInt(result_uint, false) ) + uint c = ToUIntOrInt(result_uint); + result = sint(result_uint); + + if( c ) return 1; - result = static_cast( result_uint ); - - // the exception for the minimal value - if( IsSign() && result_uint == TTMATH_UINT_HIGHEST_BIT ) - return 0; - - if( (result_uint & TTMATH_UINT_HIGHEST_BIT) != 0 ) - // the value is too big - return 1; + uint mask = 0; if( IsSign() ) + { + mask = TTMATH_UINT_MAX_VALUE; result = -result; + } - return 0; + return ((result & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT)) ? 0 : 1; } +private: + /*! - this method converts 'this' into 'result' + an auxiliary method for converting 'this' into 'result' if the value is too big this method returns a carry (1) */ template - uint ToInt(Int & result) const + uint ToUIntOrInt(UInt & result) const { result.SetZero(); @@ -2350,7 +2399,7 @@ public: if( exponent > maxbit + sint(int_size*TTMATH_BITS_PER_UINT) ) // if exponent > (maxbit + sint(int_size*TTMATH_BITS_PER_UINT)) the value can't be passed - // into the 'Int' type (it's too big) + // into the 'UInt' type (it's too big) return 1; if( exponent <= maxbit ) @@ -2374,30 +2423,79 @@ public: { uint index = how_many_bits / TTMATH_BITS_PER_UINT; - for(uint i=0 ; i maxbit + sint(int_size*TTMATH_BITS_PER_UINT) )" + // but gcc doesn't understand our types - exponent is Int<> + + for(uint i=0 ; i min; - min.SetMin(); + return 0; + } - if( result == min ) - return 0; - } - if( (result.table[int_size-1] & TTMATH_UINT_HIGHEST_BIT) != 0 ) - // the value is too big +public: + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToUInt(UInt & result) const + { + uint c = ToUIntOrInt(result); + + if( c ) return 1; if( IsSign() ) - result.ChangeSign(); + return 1; + } - return 0; + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToInt(UInt & result) const + { + return ToUInt(result); + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToInt(Int & result) const + { + uint c = ToUIntOrInt(result); + + if( c ) + return 1; + + uint mask = 0; + + if( IsSign() ) + { + result.ChangeSign(); + mask = TTMATH_UINT_MAX_VALUE; + } + + return ((result.table[int_size-1] & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT))? 0 : 1; } @@ -2439,7 +2537,7 @@ public: /*! a method for converting 'sint' to this class */ - void FromInt(sint value) + uint FromInt(sint value) { bool is_sign = false; @@ -2453,6 +2551,8 @@ public: if( is_sign ) SetSign(); + + return 0; } @@ -2487,12 +2587,8 @@ public: #ifdef TTMATH_PLATFORM32 - void FromDouble(double value) + uint FromDouble(double value) { - // sizeof(double) should be 8 (64 bits), this is actually not a runtime - // error but I leave it at the moment as is - TTMATH_ASSERT( sizeof(double) == 8 ) - // I am not sure what will be on a platform which has // a different endianness... but we use this library only // on x86 and amd (intel) 64 bits (as there's a lot of assembler code) @@ -2562,6 +2658,8 @@ public: SetZero(); } } + + return 0; // never be a carry } @@ -2602,12 +2700,8 @@ private: public: // 64bit platforms - void FromDouble(double value) + uint FromDouble(double value) { - // sizeof(double) should be 8 (64 bits), this is actually not a runtime - // error but I leave it at the moment as is - TTMATH_ASSERT( sizeof(double) == 8 ) - // I am not sure what will be on a plaltform which has // a different endianness... but we use this library only // on x86 and amd (intel) 64 bits (as there's a lot of assembler code) @@ -2671,6 +2765,8 @@ public: SetZero(); } } + + return 0; // never be a carry } private: @@ -2698,6 +2794,139 @@ private: public: + /*! + this method converts from float to this class + */ + uint FromFloat(float value) + { + return FromDouble(double(value)); + } + + + /*! + this method converts from this class into the 'double' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + if the value is too small: + 'result' will be 0 + */ + double ToDouble() const + { + double result; + + ToDouble(result); + + return result; + } + + +private: + + + /*! + an auxiliary method to check if the float value is +/-infinity + we provide this method because isinf(float) in only in C99 language + + description taken from: http://www.psc.edu/general/software/packages/ieee/ieee.php + + The IEEE single precision floating point standard representation requires a 32 bit word, + which may be represented as numbered from 0 to 31, left to right. + The first bit is the sign bit, S, the next eight bits are the exponent bits, 'E', + and the final 23 bits are the fraction 'F': + + S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF + 0 1 8 9 31 + + The value V represented by the word may be determined as follows: + + * If E=255 and F is nonzero, then V=NaN ("Not a number") + * If E=255 and F is zero and S is 1, then V=-Infinity + * If E=255 and F is zero and S is 0, then V=Infinity + * If 0> 23) & 0xff) == 0xff ) + { + if( (temp.u & 0x7FFFFF) == 0 ) + return true; // +/- infinity + } + + return false; + } + + +public: + + /*! + this method converts from this class into the 'float' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + if the value is too small: + 'result' will be 0 + */ + float ToFloat() const + { + float result; + + ToFloat(result); + + return result; + } + + + /*! + this method converts from this class into the 'float' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + and the method returns 1 + if the value is too small: + 'result' will be 0 + and the method returns 1 + */ + uint ToFloat(float & result) const + { + double result_double; + + uint c = ToDouble(result_double); + result = float(result_double); + + if( result == -0.0f ) + result = 0.0f; + + if( c ) + return 1; + + // although the result_double can have a correct value + // but after converting to float there can be infinity + + if( IsInf(result) ) + return 1; + + if( result == 0.0f && result_double != 0.0 ) + // result_double was too small for float + return 1; + + return 0; + } + /*! this method converts from this class into the 'double' @@ -2711,10 +2940,6 @@ public: */ uint ToDouble(double & result) const { - // sizeof(double) should be 8 (64 bits), this is actually not a runtime - // error but I leave it at the moment as is - TTMATH_ASSERT( sizeof(double) == 8 ) - if( IsZero() ) { result = 0.0; @@ -2733,7 +2958,7 @@ public: if( exponent >= 1024 - e_correction ) { // +/- infinity - result = ToDouble_SetDouble( 0, 2047, 0, true); + result = ToDouble_SetDouble( IsSign(), 2047, 0, true); return 1; } @@ -2872,6 +3097,17 @@ public: } + /*! + an operator= for converting 'float' to this class + */ + Big & operator=(float value) + { + FromFloat(value); + + return *this; + } + + /*! an operator= for converting 'double' to this class */ @@ -2909,6 +3145,14 @@ public: } + /*! + a constructor for converting 'float' to this class + */ + Big(float value) + { + FromFloat(value); + } + #ifdef TTMATH_PLATFORM32 diff --git a/ttmath/ttmathint.h b/ttmath/ttmathint.h index 39e5247..0b3fc96 100644 --- a/ttmath/ttmathint.h +++ b/ttmath/ttmathint.h @@ -1134,6 +1134,30 @@ public: } + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToUInt(uint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (result & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToInt(uint & result) const + { + return ToUInt(result); + } + /*! this method converts the value to sint type @@ -1157,6 +1181,36 @@ public: #ifdef TTMATH_PLATFORM32 + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToUInt(ulint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + if( value_size == 2 ) + return (UInt::table[1] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToInt(ulint & result) const + { + return ToUInt(result); + } + + /*! this method converts the value to slint type (64 bit signed integer) can return a carry if the value is too long to store it in slint type @@ -1196,7 +1250,37 @@ public: #ifdef TTMATH_PLATFORM64 /*! - this method converts the value to a 32 signed integer + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToUInt(unsigned int & result) const + { + // !! need testing + + uint c = UInt::ToUInt(result); + + if( c ) + return 1; + + return (int(result) < 0) ? 1 : 0; + } + + + /*! + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToInt(unsigned int & result) const + { + // !! need testing + return ToUInt(result); + } + + + /*! + this method converts the value to a 32 bit signed integer can return a carry if the value is too long to store it in this type *** this method is created only on a 64 bit platform *** */