-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Fix incorrect results from floating-point division (fld, cld, div) #60497
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
base: master
Are you sure you want to change the base?
Conversation
|
unfortunately does not solve the whole issue |
|
@adienes @Seelengrab Float64 cannot represent the value 1.1258999068416249e16. julia> x, y = 1.125899906841625e15, 0.1
(1.125899906841625e15, 0.1)
julia> prevfloat(x/y), x/y, nextfloat(x/y)
(1.1258999068416248e16, 1.125899906841625e16, 1.1258999068416252e16)As already mentioned in the issue :
|
|
|
Could you at least explain why do you think it should be the correct result ?
julia> 1.125899906841625e15 / 0.1
1.125899906841625e16
julia> (1.125899906841625e15 - 1.125899906841625e15 % 0.1) / 0.1
1.125899906841625e16
julia> 1.125899906841625e15 / 0.1 - (1.125899906841625e15 % 0.1) / 0.1
1.125899906841625e16Getting Also, |
|
so your concern is that since the condition is impossible (since the largest integer not exceeding I don't mean that question as sophistry; it is a fair argument and one that has appeared a few times to attempt to justify behavior of accidental integer overflow e.g. in #34325, also discussed in #50486 . but I think due to the high performance needs of functions like there are basically three paths forward as I see it:
unfortunately the first two paths would be disastrous for performance and type-stability and are non-starters, so by process of elimination that leaves only the third. I would suggest an amendment to the docstring --- and, I believe, this is what most discussion participants in #49450 were implicitly assuming already --- to something like
and under this definition, a type-stable and fast |
|
You are missing the point : the floating-point division I'm not comfortable with the idea that
1.125899906841625e16 still satisfies the premise. Also, the current implementation gives the same results for those case where You didn't explain why 1.1258999068416248e16 should be preferred, and what is violating the rounding conditions. |
|
here's what I'm looking at: so to my eyes the "correct" integer would be as a side note:
I would invite you to use phrasing that more clearly reaffirms that we are collaborating together to solve the bug, rather than that we are arguing 🙂 |
doesn't sound like "we are collaborating together to solve the bug" but rather like "we are arguing" about the legitimacy of a PR that doesn't improve anything or even asks to break things because my concern is. Again, the implementation I propose fixes cld, fld, div, giving an inexact result where the exact result is representable, and for those edge cases where |
|
well I can appreciate the fervent defense and I do apologize for having given off the wrong impression during review. this might be a fun one to discuss on triage, so I'll tag for review on the next meeting |
|
FWIW, the other partial fix in #49637 for Float16 & Float32 as far as I can tell makes the same decision that @adienes mentions, in particular see #49637 (comment) So it would be good to be consistent and have Float64 behave the same. |
|
The comment you mention doesn't involve x,y such that Example 1 julia> x, y = 1.0, 0.1
(1.0, 0.1)
julia> a, r = divrem(x, y, RoundDown) # accurate
(9.0, 0.09999999999999995)
julia> a + r/y == x/y, a + r/y, x/y # accurate, consistent
(true, 10.0, 10.0)Example 2 julia> x, y = 1.125899906841625e15, 0.1
(1.125899906841625e15, 0.1)
julia> a, r = divrem(x, y, RoundDown) # inaccurate (intrinsic roundoff)
(1.125899906841625e16, 0.03750000000005546)
julia> a + r/y == x/y, a + r/y, x/y # inaccurate (intrinsic roundoff), consistent
(true, 1.125899906841625e16, 1.125899906841625e16)Now from example 2, let say because the remainder is not zero we force julia> a = 1.1258999068416248e16 # inaccurate (arbitrary roundoff)
1.1258999068416248e16
julia> a + r/y == x/y, a + r/y, x/y # inaccurate (arbitrary roundoff), inconsistent
(false, 1.1258999068416248e16, 1.125899906841625e16) |
|
Regarding the behavior of julia> x, y = 2.096153f6, 0.1f0
(2.096153f6, 0.1f0)
julia> round(Float64(x) / Float64(y), RoundDown)
2.0961529e7
julia> Float32(2.0961529e7) # rounded down
2.0961528f7julia> x, y = nextfloat(x), y + 2*eps(y)
(2.0961531f6, 0.10000002f0)
julia> round(Float64(x) / Float64(y), RoundDown)
2.0961527e7
julia> Float32(2.0961527e7) # rounded up
2.0961528f7The rounding for non-representable numbers is done by If the decision was made to round down/up further for non representable numbers, we should have a partial fix that makes the "final" rounding explicit : div(x::Float32, y::Float32, r::RoundingMode) = Float32(round(Float64(x) / Float64(y), r), r)
div(x::Float16, y::Float16, r::RoundingMode) = Float16(round(Float32(x) / Float32(y), r), r)Not only is this not the case, but the comment that comes with that fix tells us cases where # Vincent Lefèvre: "The Euclidean Division Implemented with a Floating-Point Division and a Floor"
# https://inria.hal.science/inria-00070403
# Theorem 1 implies that the following are exact if eps(x/y) <= 1
div(x::Float32, y::Float32, r::RoundingMode) = Float32(round(Float64(x) / Float64(y), r))Also, #49450 was closed as completed after the Float16/Float32 fix, and reopened shortly after only to address inexact results with Float64 and mixed types involving Float64, not to address those cases where Now, if we were to leave those edge cases aside for Float64 as they are for Float16/Float32, the new fix proposed here should work seamlessly for all of them (I mean without the partial fix, but I haven't tested). In the other case, |
|
as a note for triage: it does not seem like there is a whole ton of precedent to draw from. but I guess there is one in Common Lisp: so they currently do the same thing Julia does here (and round upwards < eps, rather than downwards > eps) |
This addresses #49450 (more details in this comment).