From 72052420dd49c35180c3233955d6385456955a67 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Sat, 5 Dec 2009 18:13:53 +0000 Subject: [PATCH] fixed: the parser didn't use characters for changing the base (# and &) those characters were skipped (this bug was introduced in 0.9.0) added: to Big::ToString() - additional rounding when conv.base_round is used if the value is not an integer we calculate how many valid digits there are after the comma operator (in conv.base radix) and then we skipped the rest digits, after skipping the base-rounding is made this helps to print values which have some last clear bits in the mantissa consider this 32 bit value: (binary)0.00011100001010001111010111000000000 which has mantissa equal: (binary)11100001010001111010111000000000 (32 bits) previous the ToString() method gave: (decimal)0.10999999[...] now we have: (decimal)0.11 added: Parser::SetSmallToZero(bool zero) (default true) if true then the parser changes small values into zero small value means: - if the mantissa of the value consists only of one, two or three set bits - and these bits are next to each other - and the exponent is smaller than about 2 times the number of bits from the mantissa this helps to correctly calculate expressions such as: "0.80-3*0.34+0.22" now the parser gives zero (previous there was a value very closed to zero) added: UInt::FindLowestBit(uint & table_id, uint & index) /temporary version - asm version is missing / git-svn-id: svn://ttmath.org/publicrep/ttmath/trunk@256 e52654a7-88a9-db11-a3e9-0013d4bc506e --- CHANGELOG | 23 ++++- ttmath/ttmathbig.h | 207 ++++++++++++++++++++++++++++++++++++++---- ttmath/ttmathparser.h | 87 +++++++++++++++++- ttmath/ttmathuint.h | 50 +++++++++- 4 files changed, 345 insertions(+), 22 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8add038..fc37261 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,29 @@ -Version 0.9.1 (2009.12.02): +Version 0.9.1 prerelease (2009.12.05): * fixed: the parser didn't use characters for changing the base (# and &) those characters were skipped (this bug was introduced in 0.9.0) + * added: to Big::ToString() - additional rounding when conv.base_round is used + if the value is not an integer we calculate how many valid digits there are + after the comma operator (in conv.base radix) and then we skipped the rest + digits, after skipping the base-rounding is made + this helps to print values which have some last clear bits in the mantissa + consider this 32 bit value: + (binary)0.00011100001010001111010111000000000 + which has mantissa equal: (binary)11100001010001111010111000000000 (32 bits) + previous the ToString() method gave: (decimal)0.10999999[...] + now we have: (decimal)0.11 + * added: Parser::SetSmallToZero(bool zero) (default true) + if true then the parser changes small values into zero + small value means: + - if the mantissa of the value consists only of one, two or three set bits + - and these bits are next to each other + - and the exponent is smaller than about 2 times the number of bits from the mantissa + this helps to correctly calculate expressions such as: "0.80-3*0.34+0.22" + now the parser gives zero (previous there was a value very closed to zero) + * added: UInt::FindLowestBit(uint & table_id, uint & index) + /temporarily version - asm version is missing / + Version 0.9.0 (2009.11.25): * added: support for wide characters (wchar_t, std::wstring) * added: Big::IsInteger() diff --git a/ttmath/ttmathbig.h b/ttmath/ttmathbig.h index 8a879c3..758d3ef 100644 --- a/ttmath/ttmathbig.h +++ b/ttmath/ttmathbig.h @@ -3069,12 +3069,14 @@ private: (this formula means that the number of bits in the base is greater than one) */ if( conv.base_round && conv.base!=2 && conv.base!=4 && conv.base!=8 && conv.base!=16 ) - if( ToString_RoundMantissa(result, conv, new_exp) ) + { + if( ToString_BaseRound(result, conv, new_exp) ) { Misc::AssignString(result, error_overflow_msg); return 1; } - + } + if( ToString_SetCommaAndExponent(result, conv, new_exp) ) { Misc::AssignString(result, error_overflow_msg); @@ -3084,6 +3086,7 @@ private: if( IsSign() ) result.insert(result.begin(), '-'); + // converted successfully return 0; } @@ -3567,23 +3570,183 @@ private: } + /*! + an auxiliary method for converting into the string + + returns log(n, 2) (logarithm with the base equal 2) + n is in the range of [2,16] + */ + double ToString_LogBase2(uint n) const + { + static double log_tab[] = { + 1.0000000000000000, + 1.5849625007211562, + 2.0000000000000000, + 2.3219280948873623, + 2.5849625007211562, + 2.8073549220576041, + 3.0000000000000000, + 3.1699250014423124, + 3.3219280948873623, + 3.4594316186372973, + 3.5849625007211562, + 3.7004397181410922, + 3.8073549220576041, + 3.9068905956085185, + 4.0000000000000000 + }; + + if( n < 2 || n > 16 ) + return 1.0; + + return log_tab[n-2]; + } + + + /*! + an auxiliary method for converting into the string + + this method is used for base!=2, base!=4, base!=8 and base!=16 + we do the rounding when the value has fraction (is not an integer) + + if the value is not an integer we calculate how many valid digits there are + after the comma operator (in conv.base radix) and then we skipped the rest + digits, after skipping the base-rounding is made + */ + template + uint ToString_BaseRound(string_type & new_man, const Conv & conv, Int & new_exp) const + { + uint table_id, bit_index, zeroes; + + // at least two digits + if( new_man.size() < 2 ) + return 0; + + // exponents should be less than zero + // if new_exp are greater than or equal to zero then the value is integer + if( !new_exp.IsSign() || !exponent.IsSign() ) + return 0; + + if( !mantissa.FindLowestBit(table_id, bit_index) ) + // mantissa is zero, it should not be zero here + return 0; + + // how many zero bits there are at the end of the mantissa + zeroes = table_id * TTMATH_BITS_PER_UINT + bit_index; + + return ToString_BaseRoundDelInvalidAndRound(new_man, conv, new_exp, zeroes); + } + + + + /*! + an auxiliary method for converting into the string + + this method is used for base!=2, base!=4, base!=8 and base!=16 + we do the rounding when the value has fraction (is not an integer) + */ + template + uint ToString_BaseRoundDelInvalidAndRound(string_type & new_man, const Conv & conv, Int & new_exp, uint zeroes) const + { + uint len, valid_bits, del; + + del = 0; + + // how many bits there are in the mantissa + len = man * TTMATH_BITS_PER_UINT; + + if( exponent > -sint(len) ) + // but bits only after the comma operator + len = uint(-exponent.ToInt()); // exponent is also less than zero + + valid_bits = 0; + + if( len > zeroes ) + valid_bits = len - zeroes; + + if( valid_bits == 0 ) + // oops, this is an integer value (the value was not Standardized) + return 0; + + // the rest digits (in conv.base radix) will be cut off from new_man + // but at least two characters must be left + del = ToString_BaseRoundDelInvalid(new_man, conv.base, new_exp, valid_bits); + + // rounding from the last digit (the last digit will be deleted) + uint c = ToString_RoundMantissa(new_man, conv, new_exp); + + if( del > 0 ) + { + if( conv.trim_zeroes ) + c += new_exp.Add(del); + else + new_man.append(del, '0'); + } + + return c; + } + + + /*! + an auxiliary method for converting into the string + + returning how many digits were deleted + */ + template + uint ToString_BaseRoundDelInvalid( string_type & new_man, + uint radix, + Int & new_exp, + uint valid_bits) const + { + uint del, del_max, new_exp_abs, valid_digits; + + // calculating how many valid digits there are after the comma operator + // (in new_man where the radix is conv.base) + sint v = sint(double(valid_bits) / ToString_LogBase2(radix) - 2.0); + valid_digits = uint( (v<=1)? 1 : v ); // minimum 1 + del = 0; + + if( new_man.size() >= TTMATH_UINT_HIGHEST_BIT ) + // oops, new_man.size() is too big for calculating + return del; + + if( new_man.size() < 3 ) + // it must be at least three characters in the new_man + return del; + + if( new_exp <= -sint(TTMATH_UINT_HIGHEST_BIT) ) + // oops, new_exp is too big for calculating + return del; + + new_exp_abs = uint(-new_exp.ToInt()); // new_exp is also less than zero + + if( new_exp_abs > valid_digits ) + del = new_exp_abs - valid_digits; + + // miminum two characters should be left in new_man + // also new_man.size() is > 2 + del_max = (new_man.size()==3)? 1 : new_man.size()-2; + + if( del > del_max ) + del = del_max; + + if( del > 0 ) + new_man.erase(new_man.size()-del); // deleting del characters at the end of new_man + + return del; + } + + /*! an auxiliary method for converting into the string this method roundes the last character from the new mantissa - (it's used in systems where the base is different from 2) */ template uint ToString_RoundMantissa(string_type & new_man, const Conv & conv, Int & new_exp) const { // we must have minimum two characters - if( new_man.length() < 2 ) - return 0; - - // the rounding is only made when new_exp is less than zero - // if new_exp is greater or equal zero then the value is integer and - // don't have to be rounded - if( !new_exp.IsSign() ) + if( new_man.size() < 2 ) return 0; typename string_type::size_type i = new_man.length() - 1; @@ -3640,6 +3803,7 @@ private: } + /*! an auxiliary method for converting into the string @@ -3767,9 +3931,12 @@ private: if( new_man.empty() ) return; - new_man.insert( new_man.begin()+1, static_cast(conv.comma) ); + if( new_man.size() > 1 ) + { + new_man.insert( new_man.begin()+1, static_cast(conv.comma) ); + ToString_CorrectDigitsAfterComma(new_man, conv); + } - ToString_CorrectDigitsAfterComma(new_man, conv); ToString_Group_man(new_man, conv); if( conv.base == 10 ) @@ -4194,7 +4361,7 @@ private: { sint character; uint c = 0, index = 1; - Big part, power, old_value, base_( conv.base ); + Big sum, part, power, old_value, base_( conv.base ); // we don't remove any white characters here @@ -4203,9 +4370,15 @@ private: old_value.SetZero(); power.SetOne(); + sum.SetZero(); for( ; true ; ++source, ++index ) { + if( index == 98 ) + { + index = 98; + } + if( conv.group!=0 && *source==static_cast(conv.group) ) continue; @@ -4231,12 +4404,12 @@ private: bool testing = (character != 0 && (index % 5) == 0); if( testing ) - old_value = *this; + old_value = sum; // there actually shouldn't be a carry here - c += Add( part ); + c += sum.Add( part ); - if( testing && old_value == *this ) + if( testing && old_value == sum ) // after adding 'part' the value has not been changed // there's no sense to add any next parts break; @@ -4247,6 +4420,8 @@ private: // we should set a correct value of 'source' now for( ; Misc::CharToDigit(*source, conv.base) != -1 ; ++source ); + c += Add(sum); + return (c==0)? 0 : 1; } diff --git a/ttmath/ttmathparser.h b/ttmath/ttmathparser.h index 1bb6537..08f7904 100644 --- a/ttmath/ttmathparser.h +++ b/ttmath/ttmathparser.h @@ -464,6 +464,18 @@ int param_sep; */ bool calculated; + +/*! + small values will be set to zero + small value means: + - if the mantissa of the value consists only of one, two or three set bits + - and these bits are next to each other + - and the exponent is smaller than about 2 times the number of bits from the mantissa + default: true +*/ +bool small_to_zero; + + /*! we're using this method for reporting an error */ @@ -1737,6 +1749,47 @@ return is_it_name_of_function; } +/*! + small values will be set to zero + small value means: + - if the mantissa of the value consists only of one, two or three set bits + - and these bits are next to each other + - and the exponent is smaller than about 2 times the number of bits from the mantissa +*/ +void SmallToZero(ValueType & value) +{ + if( value.IsNan() || value.IsZero() ) + return; + + int man_size = int(value.mantissa.Size() * TTMATH_BITS_PER_UINT); + int epsilon = man_size / 10; + + if( value.exponent >= -(2*man_size - epsilon) ) + return; + + uint id, bit; + uint highest, lowest; + + value.mantissa.FindLeadingBit(id, bit); + highest = id * TTMATH_BITS_PER_UINT + bit; + + value.mantissa.FindLowestBit(id, bit); + lowest = id * TTMATH_BITS_PER_UINT + bit; + + if( highest < lowest ) + // oops, something wrong + return; + + uint bits = highest-lowest + 1; + + // max two set bits (these bits are next to each other) + // maybe in the future this can be changed to 3 or more + // may this should be depended from value.mantissa.Size() ? + if( bits <= 3 ) + value.SetZero(); +} + + /*! we're reading a numerical value directly from the string */ @@ -1884,13 +1937,15 @@ int character; this value is from a variable or directly from the string */ result.type = Item::numerical_value; - + if( result.sign ) { result.value.ChangeSign(); result.sign = false; } - + + if( small_to_zero ) + SmallToZero(result.value); return 0; } @@ -2128,6 +2183,9 @@ uint res; */ Error( err_internal_error ); } + + if( small_to_zero ) + SmallToZero(value1); } @@ -2308,6 +2366,9 @@ int index; // the result of a function will be on 'stack[index-1]' // and then at the end we'll set the correct type (numerical value) of this element CallFunction(stack[index-1].function_name, amount_of_parameters, index); + + if( small_to_zero ) + SmallToZero(stack[index-1].value); } else { @@ -2335,7 +2396,7 @@ int index; stack[index-1].value.ChangeSign(); stack[index-1].type = Item::numerical_value; - + /* the pointer of the stack will be pointing on the next (non-existing now) element @@ -2511,6 +2572,7 @@ Parser(): default_stack_size(100) comma = '.'; comma2 = ','; param_sep = 0; + small_to_zero = true; CreateFunctionsTable(); CreateVariablesTable(); @@ -2534,6 +2596,7 @@ Parser & operator=(const Parser & p) comma = p.comma; comma2 = p.comma2; param_sep = p.param_sep; + small_to_zero = p.small_to_zero; /* we don't have to call 'CreateFunctionsTable()' etc. @@ -2722,6 +2785,24 @@ bool Calculated() return calculated; } + +/*! + small values will be set to zero + (all values, variables, functions and results from calculating are checked) + + small value means: + - if the mantissa of the value consists only of one, two or three set bits + - and these bits are next to each other + - and the exponent is smaller than about 2 times the number of bits from the mantissa + default: true +*/ +void SetSmallToZero(bool zero) +{ + small_to_zero = zero; +} + + + }; diff --git a/ttmath/ttmathuint.h b/ttmath/ttmathuint.h index 311e945..c56062a 100644 --- a/ttmath/ttmathuint.h +++ b/ttmath/ttmathuint.h @@ -627,7 +627,7 @@ public: if 'this' is not zero: return value - true 'table_id' - the index of a word <0..value_size-1> - 'index' - the index of this set bit in the word <0..31> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) if 'this' is zero: return value - false @@ -645,13 +645,59 @@ public: return false; } - // table[table_id] != 0 + // table[table_id] is different from 0 index = FindLeadingBitInWord( table[table_id] ); return true; } + /*! + this method looks for the smallest set bit + + result: + if 'this' is not zero: + return value - true + 'table_id' - the index of a word <0..value_size-1> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) + + if 'this' is zero: + return value - false + both 'table_id' and 'index' are zero + */ + bool FindLowestBit(uint & table_id, uint & index) const + { + for(table_id=0 ; table_id= value_size ) + { + // is zero + index = 0; + table_id = 0; + + return false; + } + + // table[table_id] is different from 0 + + // !! need asm version: + // index = FindSmallestBitInWord( table[table_id] ); + + index = 0; + uint temp = table[table_id]; + + while( (temp & 1) == 0 ) + { + index += 1; + temp >>= 1; + } + + // !! + + return true; + } + + /*! setting the 'bit_index' bit