Browse Source

Fix floating-point input from decimal string.

A bug was introduced in 3480230e: The divide-and-conquer method multiplies
with a power of the base, but this power is one too much if there is a
decimal point. This may happen because digits_to_I(...) is also called
from read_float(...). As a result, the number containd spurious zeros
(in the base used for reading it).

Thanks to Thomas Luthe <tluthe@physik.uni-bielefeld.de>.
master
Richard Kreckel 10 years ago
parent
commit
44e77b58d0
  1. 36
      src/integer/conv/cl_I_from_digits.cc

36
src/integer/conv/cl_I_from_digits.cc

@ -18,7 +18,7 @@ static const cl_I digits_to_I_base2 (const char * MSBptr, uintC len, uintD base)
{ {
// base is a power of two: write the digits from least significant // base is a power of two: write the digits from least significant
// to most significant into the result NUDS. Result needs // to most significant into the result NUDS. Result needs
// 1+ceiling(len*log(base)/(intDsize*log(2))) or some more digits
// 1+ceiling(len*log(base)/(intDsize*log(2))) or some more digits.
CL_ALLOCA_STACK; CL_ALLOCA_STACK;
var uintD* erg_MSDptr; var uintD* erg_MSDptr;
var uintC erg_len; var uintC erg_len;
@ -64,7 +64,7 @@ static const cl_I digits_to_I_base2 (const char * MSBptr, uintC len, uintD base)
static const cl_I digits_to_I_baseN (const char * MSBptr, uintC len, uintD base) static const cl_I digits_to_I_baseN (const char * MSBptr, uintC len, uintD base)
{ {
// base is not a power of two: Add digits one by one. Result nees
// base is not a power of two: Add digits one by one. Result needs
// 1+ceiling(len*log(base)/(intDsize*log(2))) or some more digits. // 1+ceiling(len*log(base)/(intDsize*log(2))) or some more digits.
CL_ALLOCA_STACK; CL_ALLOCA_STACK;
var uintD* erg_MSDptr; var uintD* erg_MSDptr;
@ -121,7 +121,6 @@ static const cl_I digits_to_I_baseN (const char * MSBptr, uintC len, uintD base)
var uintD factor = 1; var uintD factor = 1;
while (chx < power_table[base-2].k && len > 0) { while (chx < power_table[base-2].k && len > 0) {
var uintB ch = *(const uintB *)MSBptr; MSBptr++; // next character var uintB ch = *(const uintB *)MSBptr; MSBptr++; // next character
if (ch!='.') { // skip decimal point
// Compute value of ('0'-'9','A'-'Z','a'-'z'): // Compute value of ('0'-'9','A'-'Z','a'-'z'):
ch = ch-'0'; ch = ch-'0';
if (ch > '9'-'0') { // not a digit? if (ch > '9'-'0') { // not a digit?
@ -133,7 +132,6 @@ static const cl_I digits_to_I_baseN (const char * MSBptr, uintC len, uintD base)
factor = factor*base; factor = factor*base;
newdigit = base*newdigit+ch; newdigit = base*newdigit+ch;
chx++; chx++;
}
len--; len--;
} }
var uintD carry = mulusmall_loop_lsp(factor,erg_LSDptr,erg_len,newdigit); var uintD carry = mulusmall_loop_lsp(factor,erg_LSDptr,erg_len,newdigit);
@ -146,11 +144,8 @@ static const cl_I digits_to_I_baseN (const char * MSBptr, uintC len, uintD base)
return NUDS_to_I(erg_MSDptr,erg_len); return NUDS_to_I(erg_MSDptr,erg_len);
} }
const cl_I digits_to_I (const char * MSBptr, uintC len, uintD base)
static const cl_I digits_to_I_divconq (const char * MSBptr, uintC len, uintD base)
{ {
if ((base & (base-1)) == 0) {
return digits_to_I_base2(MSBptr, len, base);
} else {
// This is quite insensitive to the breakeven point. // This is quite insensitive to the breakeven point.
// On a 1GHz Athlon I get approximately: // On a 1GHz Athlon I get approximately:
// base 3: breakeven around 25000 // base 3: breakeven around 25000
@ -167,12 +162,33 @@ const cl_I digits_to_I (const char * MSBptr, uintC len, uintD base)
break; break;
len_B = len_B*2; len_B = len_B*2;
} }
return digits_to_I(MSBptr,len-len_B,base)*p->base_pow
+digits_to_I(MSBptr+len-len_B,len_B,base);
return digits_to_I_divconq(MSBptr,len-len_B,base) * p->base_pow
+ digits_to_I_divconq(MSBptr+len-len_B,len_B,base);
} else { } else {
return digits_to_I_baseN(MSBptr, len, base); return digits_to_I_baseN(MSBptr, len, base);
} }
} }
const cl_I digits_to_I (const char * MSBptr, uintC len, uintD base)
{
if ((base & (base-1)) == 0) {
return digits_to_I_base2(MSBptr, len, base);
} else {
// digits_to_I_divconq cannot handle decimal points, so remove it here
CL_ALLOCA_STACK;
const uintD * digits_copy;
num_stack_alloc(len,,digits_copy=);
char * copy_ptr = (char *)digits_copy;
uintC n = 0;
for (uintC i = 0; i < len; ++i) {
const char ch = MSBptr[i];
if (ch != '.') { // skip decimal point
copy_ptr[n] = ch;
n++;
}
}
return digits_to_I_divconq((const char*)digits_copy, n, base);
}
} }
} // namespace cln } // namespace cln
Loading…
Cancel
Save