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: ~Nf gives surprising result for floats with negative exponents. #2771

Open
tmerr opened this issue Jan 15, 2025 · 8 comments
Open

format: ~Nf gives surprising result for floats with negative exponents. #2771

tmerr opened this issue Jan 15, 2025 · 8 comments

Comments

@tmerr
Copy link

tmerr commented Jan 15, 2025

?- format("~3f", [1.0e-6]).
1.0e-   true.

Expected result is 0.000.

@triska
Copy link
Contributor

triska commented Jan 15, 2025

Great catch, thank you a lot for reporting this!

Also for positive exponents:

?- format("~3f", [1.0e234]).
1.0e2   true.

I think some way to reason a bit more about floats is needed (related: #1343). For example, we can do:

?- X is float_integer_part(1.0e234).
   X = 1.0e234.

But how do we even get at the integer here so that we can format it?

@UWN
Copy link

UWN commented Jan 16, 2025

But how do we even get at the integer here so that we can format it?

First, #2772 must be resolved which effectively gives the digits in front of the dot.

triska added a commit to triska/scryer-prolog that referenced this issue Jan 16, 2025
This addresses mthom#2771, using a new internal predicate resorting to
to_string().

Many thanks to Trevor Merrifield for reporting this issue!

There is still room for improvements: With better support for
inspecting floats, more of this logic can be moved to Prolog; also,
there may be a way to obtain the float with greater precision,
and with fewer needed Rust primitives.
@triska
Copy link
Contributor

triska commented Jan 16, 2025

Could you please take a look at triska@d9a7305 and see if it solves your issue?

With this patch, I get:

?- format("~3f", [1.0e-6]).
0.000   true.

@tmerr
Copy link
Author

tmerr commented Jan 17, 2025

Yes, it works for the failing case! I tested the patch on top of master.

$ curl --location https://github.com/triska/scryer-prolog/commit/d9a7305f72981c7261eef5827c1123f4fd172948.patch | git am
$ cargo build --release
$ ./target/release/scryer-prolog
?- format("~3f", [1.0e-6]).
0.000   true.

I don't have the background to comment on the relationship to #2772 .

@UWN
Copy link

UWN commented Jan 17, 2025

This patch is incompatible with the existing implementations that are based on double precision IEEE floats in base 2 and whose integers are not bounded. Namely: B, Ciao, ECLiPSe, SICStus, SWI, Trealla and who all are fine with #2772.

?- format("~f",[8.1e22]).
   outputs("81000000000000000000000.000000"), unexpected.
   outputs("81000000000000006291456.000000"). % expected, but not found

Why? The integers above 2^53 < 9.007e15 cannot all be represented exactly with floats. And the only thing one can thus do is to print out the exact value. Only floats with base 10 could represent that value exactly, which so far not a single Prolog system supports. The standard does permit this in 7.1.3.

Note that the digits before the dot have to be the same as the result of:

?- X is truncate(8.1e22).
   X = 9223372036854775807, unexpected.
   X = 81000000000000006291456.  % expected, but not found

In addition to above systems, IF, IV, and YAP produce the same result.

@UWN
Copy link

UWN commented Jan 18, 2025

Just to be sure: there are at least two separate cases to distinguish:
when the fractional part is zero: do just one truncate for the integer part and fill zeroes after the dot. And otherwise, multiply the fractional part with 10^n (no exponent overflow is possible in this case) and round to an integer, if that value is >= 10^n, this needs to be added to the integer part. (Have not thought about negatives, though)

@triska
Copy link
Contributor

triska commented Jan 18, 2025

I have one elementary question about this: Why must these horrific considerations be made at all: I interpret what Rust's to_string() does as "the shortest possible string representation within one eps", and I understand from this issue that this error is not acceptable, but why is such an error even acceptable for Rust programmers?? In other words, why is there no Rust functionality to solve such an elementary problem without having to think of negative numbers etc. (and who knows what else can even arise with floats)?

@UWN
Copy link

UWN commented Jan 18, 2025

IEEE floats came out 1985. The first paper to accurately print them decimally was 1990. Paper, not downloadable program. format/2 is older. And we now want to be compatible with one specific option.

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

3 participants