-
Notifications
You must be signed in to change notification settings - Fork 136
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
Fix invalid casts in is/2 #2777
Conversation
(might as well fix |
508e901
to
a739390
Compare
Thank you a lot, this is ideal: With this I can address #2771 entirely in Prolog, based on solid underlying functionality in the core! |
1afa051
to
3b6c209
Compare
src/machine/arithmetic_ops.rs
Outdated
Ok(Number::Float(cmp::max(OrderedFloat(f1), OrderedFloat(f2)))) | ||
match OrderedFloat(f1).cmp(&OrderedFloat(f2)) { | ||
cmp::Ordering::Less => Ok(n2), | ||
cmp::Ordering::Equal => Ok(Number::Float(OrderedFloat(f1))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intentional/relevant that the returned value in the equal case has changed?
I think this would only be relevant for an edge case 1 of 0.0
and -0.0
which OrderedFloat
compares as equal if I am not mistaken.
cmp::max
specifies that in the case of equality the second argument is returned, now the first argument is returned.
Footnotes
-
NaN
values should have already been eliminated byresult_f
↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is replicating the behavior of SWI-Prolog, where I return a floating-point value if both terms are equal. The idea (as far as I understand) is to make max(1, 1.0)
return the same value as max(1.0, 1)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That still leaves max(0.0, -0.0)
and max(-0.0, 0.0)
, SWI-Prolog treats 0.0 and 0 to be larger than -0.0, as defined by your linked documentation.
But the implementation here returns the first argument converted to float i.e. -0.0
for max(-0.0, 0)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I am not sure if one is currently able to produce -0.0
in scryer-prolog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I am not sure if one is currently able to produce
-0.0
in scryer-prolog.
In ISO 0.0
and -0.0
are the same.
?- 0.0 == -0.0.
true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In ISO 0.0 and -0.0 are the same.
While they compare as numerically equal, the SWI-Prolog documentation of max contains the following text
However, the special float -0.0 is smaller than 0.0 as well as the integer 0
Which sounds to me like max should treat 0.0 and 0 as being larger than -0.0,
though I am not sure if this is just an implementation detail or ISO required.
For FFI it would be good to to properly support unusual/invalid float values like ±infinity, ±nan and -0.0 similar to SWI-Prolog https://www.swi-prolog.org/pldoc/man?section=ieee-float which allows setting flags to switch from ISO behavior to IEEE float behavior.
(I am not trying to say that this should be implemented as part of this PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SWI is not ISO in many areas like this one.
Somehow my tests did not cover these cases, but upon refactoring the implementation of ?- X is 1 >> 64.
X = 1, unexpected.
X = 0. % expected, but not found
?- X is 1 << 64.
X = 1, unexpected.
X = 18446744073709551616. % expected, but not found
?- X is 1 << 63.
X = -9223372036854775808, unexpected.
X = 9223372036854775808. % expected, but not found I shan't exclude the fix from this PR :) |
Fixes mthom#2772. The current implementation of `rnd_i` incorrectly casts `f` (an `f64`) into an `i64`, before casting it into an `Integer`. This fixes that issue by using `Integer::try_from(f)` instead, and failing if `f` is infinite or NaN. A fixme is left for a future PR to properly handle the resulting errors in floor/1 and friends (right now they can only be triggered through FFI).
The original issue can be reproduced with `X is round(2 ^ 54 + 1) - 2 ^ 54, X = 1.`
The implementation for gcd/2 would cast the second argument to an isize.
It now behaves the same way as SWI-Prolog.
ed71062
to
f6abafe
Compare
(squashed the earlier fixups) (the push below fixes a typo) |
This extensively tests the behavior of is/2, both when compiled and in metacalls.
f6abafe
to
0cde8f9
Compare
This fixes #2772 and a few of other issues I found:
rnd_i
would first convert big floats into an i64, before converting them into anIBig
round/1
would add0.5
to big integers, losing precision beyond±2 ^ 53
gcd/2
would attempt to cast its second argument to anisize
before computing the gcd usingIBig::gcd
, triggering apanic!()
min/2
andmax/2
would return the float version of their arguments if their types didn't matchThis PR is accompanied by a test suite (that @UWN might be interested in) for the behavior of
is/2
, both after being compiled and being metacalled. Implementation-specific behavior (like bitshifting negative numbers) is excluded from this test suite.I ran the integration tests with
cargo-tarpaulin
to get a report and help me find uncovered edge cases. I chose not to test special cases likepow(integer, integer)
, since it's unclear to me whether or not smallNumber::Integer
should be returned from computations (likeX is 2 ^ 64 - 2 ^ 64
).Each of these changes (including the addition of the new test suite) were placed in their own commits.