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
This commit is contained in:
Tomasz Sowa 2009-12-05 18:13:53 +00:00
parent 321953e833
commit 72052420dd
4 changed files with 345 additions and 22 deletions

View File

@ -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()

View File

@ -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<string_type, char_type>(result, conv, new_exp) )
{
if( ToString_BaseRound<string_type, char_type>(result, conv, new_exp) )
{
Misc::AssignString(result, error_overflow_msg);
return 1;
}
}
if( ToString_SetCommaAndExponent<string_type, char_type>(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<class string_type, class char_type>
uint ToString_BaseRound(string_type & new_man, const Conv & conv, Int<exp+1> & 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<string_type, char_type>(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<class string_type, class char_type>
uint ToString_BaseRoundDelInvalidAndRound(string_type & new_man, const Conv & conv, Int<exp+1> & 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<string_type, char_type>(new_man, conv.base, new_exp, valid_bits);
// rounding from the last digit (the last digit will be deleted)
uint c = ToString_RoundMantissa<string_type, char_type>(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<class string_type, class char_type>
uint ToString_BaseRoundDelInvalid( string_type & new_man,
uint radix,
Int<exp+1> & 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<class string_type, class char_type>
uint ToString_RoundMantissa(string_type & new_man, const Conv & conv, Int<exp+1> & 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<char_type>(conv.comma) );
if( new_man.size() > 1 )
{
new_man.insert( new_man.begin()+1, static_cast<char_type>(conv.comma) );
ToString_CorrectDigitsAfterComma<string_type, char_type>(new_man, conv);
}
ToString_CorrectDigitsAfterComma<string_type, char_type>(new_man, conv);
ToString_Group_man<string_type, char_type>(new_man, conv);
if( conv.base == 10 )
@ -4194,7 +4361,7 @@ private:
{
sint character;
uint c = 0, index = 1;
Big<exp, man> part, power, old_value, base_( conv.base );
Big<exp, man> 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<char>(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;
}

View File

@ -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<ValueType> & operator=(const Parser<ValueType> & 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;
}
};

View File

@ -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 && table[table_id]==0 ; ++table_id);
if( 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