Project

General

Profile

Bug #1570

FloatStr sometimes produces NUL chars

Added by John Abbott about 3 years ago. Updated over 2 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Maths Bugs
Target version:
Start date:
28 Jan 2021
Due date:
% Done:

100%

Estimated time:
8.11 h
Spent time:

Description

Here is a concrete example:

    cout << FloatStr(BigRatFromString("531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219/531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728128"),176) << endl;

The output is

9.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999^@*10^(-9)

History

#1 Updated by John Abbott about 3 years ago

The bug is more embarrassing than dangerous...

Don't yet have a simpler failing example.

Of course, it came up while giving a demo :-(

#2 Updated by John Abbott about 3 years ago

  • Status changed from New to In Progress
  • % Done changed from 0 to 10

The exponent is wrong by 1: it should be -10.

#3 Updated by John Abbott about 3 years ago

Operating on 10*q produces the same mantissa (with NUL) but without exponent.
Operating on 100*q produces a correct result: the digit 4 instead of NUL.

Most odd!

#4 Updated by John Abbott about 3 years ago

FloatStr simply calls MantissaAndExponent10 to do the main conversion.
Indeed MantissaAndExponent10 behaves wrongly

Here are the outputs for q, 10*q and 100*q

MantExp10(sign=1, exp=-9, mant=9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, NumDigits=176)
MantExp10(sign=1, exp=-8, mant=9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, NumDigits=176)
MantExp10(sign=1, exp=-8, mant=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999994, NumDigits=176)

#5 Updated by John Abbott about 3 years ago

Aha! It seems that FloorLog10 sometimes give wrong answers... groan.
With luck that is the root cause.

#6 Updated by John Abbott about 3 years ago

  • Assignee set to John Abbott
  • % Done changed from 10 to 80

I have found what is most likely the cause (after tracing through with the debugger).

The problem was in FloorLogBase, in ptic the definition of the value delta.
For "efficiency", an approx log was computed using floating-point; delta is an estimate of how inaccurate the fp log might be.
The old estimate was 5ulp, but apparently this was not always enough.

I have now increased the estimate to 256ulp; probably even 8ulp should be enough.
The only disadvantage is that a full check will be made a bit more often -- this happens only when the supplied value is "very close to" an integer power of the base.

I have inserted an assert also for the "safe case" (where input is "far from" an integer power).

Analogous change to FloorLogBase for BigInt.

#7 Updated by John Abbott about 3 years ago

Here is a simpler failing case:

N := 10^20-1;
Q := N/10^21;
FlooLog10(Q); --> -1 instead of -2

N := 10^30-1;
Q := N/10^29;
FloorLog10(Q); --> 1 instead of 0

This code prints out many triples -- it should print out none!

 for B := 18 to 99 do N := 10^B-1; for j := 0 to 200 do F := FloorLog10(N/10^j); if F <> B-1-j then println [B,j,F]; endif; endfor; endfor;

#8 Updated by John Abbott about 3 years ago

This is not good.

I have increased the factor in the definition of delta to 64: there are still failing cases:

/**/ for B := 18 to 199 do N := 10^B-1; for j := 0 to 250 do F := FloorLog10(N/10^j); if F <> B-1-j then println [B,j,F]; endif; endfor; endfor;
[117,  116,  1]
[123,  122,  1]
[128,  127,  1]
[134,  133,  1]
[140,  139,  1]
[146,  145,  1]
[152,  151,  1]
[158,  157,  1]
[164,  163,  1]
[170,  169,  1]
[176,  175,  1]
[182,  181,  1]
[188,  187,  1]
[194,  193,  1]

The problem seems to occur only with BigRat; I have tried a similar test loop with BigInt but found no failing examples.
I now guess that log for BigRat values with large numer-denom is giving imprecise values:
this could result from a simplistic impl log(N/D) = log(N)-log(D); if log(N/D) is small but log(N) is larger than 1000 (say)
then we could indeed lose 10 bits of precision.

I'll check the log code... (sigh)

#9 Updated by John Abbott about 3 years ago

And indeed the defn of log for BigRat is simply

  return log(num(Q)) - log(den(Q));

Now I need a new defn... [facepalm]

#10 Updated by John Abbott about 3 years ago

I think everything is nearly sorted out now: the example in comment 8 now finds no problems (even over a much wider range).
I have a new fn mpq_get_d_2exp; actually there was an old one in RingQQ.C, but it was sometimes less accurate than the new one.

There is also a new fn LogAbs, and the existing log now requires arg to be positive (previously it behaved like the new LogAbs fn).

Updated some doc; added new test in test-BigRat3.C

Soon I can check in.

#11 Updated by John Abbott about 3 years ago

  • Status changed from In Progress to Feedback
  • % Done changed from 80 to 90

#12 Updated by John Abbott over 2 years ago

  • Status changed from Feedback to Closed
  • % Done changed from 90 to 100
  • Estimated time set to 8.11 h

Also available in: Atom PDF