trealla
trealla copied to clipboard
not precise enough float printing
Currently I get:
$ ./tpl -v
Trealla Prolog (c) Infradig 2020-2022, v2.2.10
$ ./tpl
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.0.
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.
N9 = 4.440892098500626e-16.
?-
Because the number is not 3.0, I expect something else than 3.0 is printed.
Like here:
/* Jekejeke Prolog 1.5.4 */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.0000000000000004.
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.
N9 = 4.440892098500626E-16.
Strange on my side printing 3.0000000000000004 is not in conflict with printing 1/10. I still get:
/* Jekejeke Prolog 1.5.4 */
?- X is 1/10.
X = 0.1.
I don't know how Java does it. I guess they neither use 17 nor 18 digits, but switch between the two, when possible. Similarly no problem in JavaScript:
/* Dogelog Player 1.0.2, JavaScript */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.0000000000000004.
?- X is 1/10.
X = 0.1.
But in Python I use a more precise (/)/2, so the results are there as follows:
/* Dogelog Player 1.0.2, Python */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.0.
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.
N9 = 0.0.
?- N9 is float(370370367037037036703703703670) / float(123456789012345678901234567890) - 3.
N9 = 4.440892098500626E-16.
?- X is 1/10.
X = 0.1.
Maybe I should give JavaScript and Java also a more precise (/)/2 ? Didn't test yet whether Python is not only precise, but also shows good rounding in their (/)/2.
Another problem with 18...
$ tpl ?- X is 1/10. X = 0.10000000000000001. ?- X is 1/3. X = 0.33333333333333331.
It seems the last digit is subject to error so should not really be printed.
$ scryer-prolog -f ?- X is 1/3. X = 0.3333333333333333.
Well usually people use the predicate format/2 if they want to see less, don't they? And for indempodent input/output the predicate write/1 needs to show more?
format(+Format, :Arguments) e Output next argument as a floating point number in exponential notation. The numeric argument specifies the precision. Default is 6 digits. Exact representation depends on the C library function printf(). This function is invoked with the format %.
e. f Floating point in non-exponential notation. The numeric argument defines the number of digits right of the decimal point. If the colon modifier (:) is used, the float is formatted using conventions from the current locale, which may define the decimal point as well as grouping of digits left of the decimal point. https://www.swi-prolog.org/pldoc/man?predicate=format/2
Concerning your examples I get, these are all systems that can also show 3.0000000000000004:
/* Java, JavaScript and Python */
?- X is 1/3.
X = 0.3333333333333333.
For example in JavaScript I use this routine:
let res = norm_float_string(num.toPrecision());
if (res.lastIndexOf("E") === -1) {
num = norm_float_string(num.toExponential());
if (num.length < res.length)
res = num;
}
The methods toPrecision() and toExponential() are called without specifying a fraction length, the routines decide by themselves whether 17 or 18 should be used.
I don't know what tricks or libraries are around in C or C++ to archive the same.
Yeah, I was thinking of something like that. I might ponder a bit.
On Thu, Sep 29, 2022 at 8:30 AM Jean-Luc-Picard-2021 < @.***> wrote:
Concerning your examples I get, all systems that can also show 3.0000000000000004:
/* Java, JavaScript and Python */ ?- X is 1/3. X = 0.3333333333333333.
For example in JavaScript I use this routine:
let res = norm_float_string(num.toPrecision()); if (res.lastIndexOf("E") === -1) { num = norm_float_string(num.toExponential()); if (num.length < res.length) res = num; }
The methods toPrecision() and toExponential() are called without specifying a fraction length, the routines decide by themselves whether 17 or 18 should be used.
— Reply to this email directly, view it on GitHub https://github.com/trealla-prolog/trealla/issues/49#issuecomment-1261528238, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFNKSEWYAJYEEYGZYXO3RGDWATBHHANCNFSM6AAAAAAQYGDQ4Y . You are receiving this because you commented.Message ID: @.***>
Ok, I've had a go at it, see devel branch.
BTW: SWI-Prolog is full of bugs, I get this strange result, a bigger numerator, leads to a smaller result?
/* SWI-Prolog 8.5.17 */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.
?- N9 is 370370367037037036703703703671 / 123456789012345678901234567890.
N9 = 2.9999999999999996.
Python doesn’t have this bug:
/* Python 3.11.0rc1 (main, Aug 8 2022, 11:30:54) */
>>> 370370367037037036703703703670 / 123456789012345678901234567890
3.0
>>> 370370367037037036703703703671 / 123456789012345678901234567890
3.0
Yes, and it's not just a printing thing there with swipl. Direct arithmetic comparison is incorrect.
On Thu, Sep 29, 2022 at 9:03 AM Jean-Luc-Picard-2021 < @.***> wrote:
BTW: SWI-Prolog is full of bugs, I get this strange result, a bigger numerator, leads to a smaller result?
/* SWI-Prolog 8.5.17 */ ?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890. N9 = 3.
?- N9 is 370370367037037036703703703671 / 123456789012345678901234567890. N9 = 2.9999999999999996.
— Reply to this email directly, view it on GitHub https://github.com/trealla-prolog/trealla/issues/49#issuecomment-1261550979, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFNKSEREIZWCT3JBJPJJ3JLWATFCLANCNFSM6AAAAAAQYGDQ4Y . You are receiving this because you commented.Message ID: @.***>
This is also a nice test case:
/* Scryer, SWI-Prolog and Jekejeke */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.7999999999999999.
/* Trealla 2.7.15 */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.79999999999999993.
I think 18 digits might be too much?
Now its overshooting in the other direction. Was expecting that when it shows 0.8, I can also subtract 0.8 and get zero?
Trealla Prolog (c) Infradig 2020-2023, v2.9.4
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.8.
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1-0.8.
X = -1.1102230246251565e-16.
Thanks to format/2 can investigate the case in more detail. The number seems to be a challenge for the "g" format:
/* Java, JavaScript and Python */
?- X is 370370367037037036703703703670 / 123456789012345678901234567890,
format('~16g~n', [X]).
3
?- X is 370370367037037036703703703670 / 123456789012345678901234567890,
format('~17g~n', [X]).
3.0000000000000004
But the other test case is not so much affected, except for a difference in Java:
/* Java, JavaScript and Python */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1, format('~16g~n', [X]).
0.7999999999999999
/* JavaScript and Python */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1, format('~17g~n', [X]).
0.79999999999999993
/* Java */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1, format('~17g~n', [X]).
0.7999999999999999
Since Trealla Prolog can produce the same:
/* Trealla Prolog 2.14.32 */
?- X is 370370367037037036703703703670 / 123456789012345678901234567890,
format('~16g~n', [X]).
3
?- X is 370370367037037036703703703670 / 123456789012345678901234567890,
format('~17g~n', [X]).
3.0000000000000004
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1, format('~16g~n', [X]).
0.7999999999999999
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1, format('~17g~n', [X]).
0.79999999999999993
Maybe there is no harm in that the top-level doesn't show that much precision.
So I am closing this ticket.
Maybe I hit the jackpot now, with this adaptive algorithm in JavaScript:
let res = num.toPrecision(16);
if (Number(res) === num) {
return shape_number(res);
} else {
return shape_number(num.toPrecision(17));
}
It now shows me, before the adaptive algorithm, it was full of nasty 2.2999999999999998, 2.7000000000000002 etc..:
?- between(1,10,N), X is (20+N)/10, write(X), nl, fail; true.
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
3.0
And these test cases are also fine I guess:
?- X is 370370367037037036703703703670 / 123456789012345678901234567890.
X = 3.0000000000000004.
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.7999999999999999.
Scryer Prolog v0.9.4-107-geaa95293 agrees with the last two results. But Trealla Prolog v2.55.10, doesn't agree with the last two results, it still shows some bogus floating point number:
?- X is 370370367037037036703703703670 / 123456789012345678901234567890.
X = 3.0.
?- X is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.0.
X = 4.4408920985006262e-16.
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.8.
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 - 0.8.
X = -1.1102230246251565e-16.
What I don't know is, whether the new adaptive algorithm has a high performance price. It is more expensive than just calling num.toPrecision(17). It will in mimimum call num.toPrecision(16)
and do the back conversion, i.e. Number(res). So unparsing has a parsing cost. And for critical numbers, it has a second unparsing via num.toPrecision(17) cost.
The division test case disagrees in recent SWI-Prolog, since in recent versions it has a more precise (/)/2 arithmetic operation implementation.
Yeah, floating-point is a pain.
I guess the problem is that going from binary to decimal, the notion of precision becomes a little bit fuzzy.
You cannot directly transate a binary precision measured in bits basically a range from 0 to 2^N for some N, to a decimal a decimal precision, basically a range from 0 to 10^N for some N.
When expanding a binary number to decimal, sometimes N=16 is enough, there is no ambigiuity in that we hit a neighbouring binary number, and sometimes we need N=17.
GNU Prolog seems to still use the non-adaptive algorithm with 17 decimal precision. It could profit from the adaptive algorithm that arbitrates between 16 and 17 decimal precision:
/* GNU Prolog 1.5.0 */
?- X is 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1.
X = 0.79999999999999993
?- 0.79999999999999993 == 0.7999999999999999.
Yes
?- X is 23/10.
X = 2.2999999999999998
?- 2.2999999999999998 == 2.3.
Yes
All discrepancies are not incorrect displays, since reparsing decimal numbers shows that they hit the same floating point values. But 0.7999999999999999 and 2.3
would be the more shorter display.