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