Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

format("~0f",[1.4]) incorrect #2779

Open
UWN opened this issue Jan 18, 2025 · 13 comments
Open

format("~0f",[1.4]) incorrect #2779

UWN opened this issue Jan 18, 2025 · 13 comments

Comments

@UWN
Copy link

UWN commented Jan 18, 2025

?- format("~0f",[1.4]).
   outputs("1"), unexpected.   % Scryer, SICStus up to 3.9, virtually all others
   outputs("1.4"), unexpected. % SICStus 3.10.0
   outputs("1.0").             % expected, but not found, SICStus since 3.11.0

This is very odd. Practically all systems including SICStus up to 3.9 print an integer, when we are asking for a float ....

@adri326
Copy link
Contributor

adri326 commented Jan 18, 2025

I would personally expect outputs("1.") or outputs("1"). The 0 here carries no information and is more confusing than anything.

Rust also only prints 1: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=63c4b1c5230712cf1cd20f4ff7cc7905

@UWN
Copy link
Author

UWN commented Jan 18, 2025

Neither 1 nor 1. is a float.

@triska
Copy link
Contributor

triska commented Jan 18, 2025

I also expect 1 from the SICStus documentation: "N digits after the decimal point", so if N is 0, there are no digits after the decimal point and there is no decimal point. UPDATE: See below.

@UWN
Copy link
Author

UWN commented Jan 18, 2025

That is quoting one phrase out of context. The entire paragraph for f reads:

(Print float in fixed-point notation.) The argument is a float, which will be printed in fixed-point notation with N digits after the decimal point. N may be zero, or negative, in which case a single zero appears after the decimal point. At least one digit appears before the decimal point and at least one after it. N defaults to 6.

Please do not confuse the concrete syntax (the actual characters) with the precision as such. Since floating point numbers have to have at least one digit before and after the dot, all floats will occupy at least three characters.

@flexoron
Copy link

flexoron commented Jan 19, 2025

?- X is (1.0/3.0)*2.999999999999999,format("~0f",[X]).
0   X = 0.9999999999999997.
?- X is (1.0/3.0)*2.9999999999999999,format("~0f",[X]).
1   X = 1.0. % vague, (it has floated)

% 0.9 printed that would be nice.
% if ~0f should print 0.0(first example) then I would prefer ~0f = ~1f
% Fixed point notation: At least one digit appears before the decimal point and at least one after it.
% At least it is a float and not an integer.

% BTW Wolframs name for these numbers here are:
% 1st example: zero point nine nine nine ... seven
% 2nd example: one (and not one point zero)

@UWN
Copy link
Author

UWN commented Jan 19, 2025

Note already that

?- X = 2.9999999999999999.
   X = 3.0.

So your second example is unrelated to rounding in format/2.

For your first example, SICStus (as expected):

| ?- X is (1.0/3.0)*2.999999999999999,format("~0f",[X]).
1.0
X = 0.9999999999999997 ? 

If you want 0.9 as result, you need an X =< 0.95 and "~1f" as the format.

| ?- X = 0.95000000000000002, format("~1f",[X]).
1.0
X = 0.9500000000000001 ? 
yes
| ?- X = 0.95000000000000001, format("~1f",[X]).
0.9
X = 0.95 ? 

@flexoron
Copy link

flexoron commented Jan 19, 2025

Ok, I see. I thought format just cut or chop off or fill up digits from results already 'calculated'.

Looks like format/2 as such is problematic

$ swipl -q
?- X = 0.9999999999999997, format("~60f",[X]).
0.999999999999999666933092612453037872910499572753906250000000 % Strange
X = 0.9999999999999997.

% While Scryer does what is expected
?- X = 0.9999999999999997, format("~60f",[X]).
0.999999999999999700000000000000000000000000000000000000000000 
X = 0.9999999999999997.
?-

@UWN
Copy link
Author

UWN commented Jan 19, 2025

SWI is right here! Please note that floats have 52/53 bits for the mantissa. And since the number is printed in base 10 (which has 2 and 5 as factors), you still need that many decimal digits to write out the actual value in full. Usually, the output is rounded to the shortest value (or one of them) that still produces the very same value (when read back).

@flexoron
Copy link

All right! IEEE 754 Standard (0.99999999999999972 and 0.99999999999999962 are identical)..
In this sense ~0f should print 0.0 (for example).
btw .0 is a float.

@UWN
Copy link
Author

UWN commented Jan 19, 2025

In this sense ~0f should print 0.0 (for example).

It should round accordingly. Thus 1.0

btw .0 is a float.

Not in Prolog.

@UWN
Copy link
Author

UWN commented Jan 20, 2025

floats have 52/53 bits for the mantissa. And since the number is printed in base 10 (which has 2 and 5 as factors), you still need that many decimal digits to write out the actual value in full.

I should correct myself. This is not the case for numbers very close to zero. There, even more digits are required. In fact, the smallest positive number is:

0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923828934583680601060115061698097530783422773183292479049825247307763759272478746560847782037344696995336470179726777175851256605511991315048911014510378627381672509558373897335989936648099411642057026370902792427675445652290875386825064197182655334472656250000

@infradig
Copy link

That is quoting one phrase out of context. The entire paragraph for f reads:

(Print float in fixed-point notation.) The argument is a float, which will be printed in fixed-point notation with N digits after the decimal point. N may be zero, or negative, in which case a single zero appears after the decimal point. At least one digit appears before the decimal point and at least one after it. N defaults to 6.

Please do not confuse the concrete syntax (the actual characters) with the precision as such. Since floating point numbers have to have at least one digit before and after the dot, all floats will occupy at least three characters.

The only other Prolog I can see correct then is CxProlog. Everything else is wrong (including Logtalk @pmoura).

@flexoron
Copy link

Which rounding method should be the default?

?- format("~0f",[0.5]).
0   true.
?- format("~0f",[1.5]).
1   true. % or 2  (say 2.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants