-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Absolutely, positively perfect ranges. Never wrong. No way. #23194
Conversation
@nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan |
@test m == length(r) | ||
# FIXME: these fail some small portion of the time | ||
@test_skip start == first(r) | ||
@test_skip stop == last(r) |
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.
guess this was over-ambitious?
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.
However, I think (not sure, actually) that we should be able to insist on
Δ > 0 ? last(r) <= stop : last(r) >= stop
But we're not quite there yet. Needs more investigation.
I'm going to be traveling for a couple of days, so let me say please don't merge yet. I think another formal property we need to insist on is that r = start:Δ:stop
range(start, Δ, length(r)) === r and this does not satisfy that. I don't think any of the changes here are wrong, so reviews are welcome, but I think there's a little more fixing/cleanup that needs doing. |
OK, this turned into a bigger deal than I wanted, but I think it's a pretty big improvement. Perhaps the most important part of this change, aside from the two bugs I fixed, was adding a whole bunch of carefully-crafted precision tests for our TwicePrecision routines. This led down a long rabbit hole, but I made a number of improvements and learned some interesting things along the way. The most important of these is that the well-known Dekker
As a consequence, this switches our I've punted on making any of the changes discussed starting at #20373 (comment). True perfection will have to wait for a future pull request. I removed the "backport" label, since this is by now far too intrusive of a change to backport; I can create a separate patch for fixing #23178 on 0.6 after this merges. |
Travis and appveyor failures seem unrelated. |
Funny that, I just cooked up a compensated multiplication procedure the other night when I was thinking about this during a bout of insomnia: minabs(x, y) = ifelse(abs(x) ≤ abs(y), x, y)
maxabs(x, y) = ifelse(abs(x) > abs(y), x, y)
function compensated_addition(x::T, y::T) where T<:AbstractFloat
z = x + y
z, minabs(x, y) - (z - maxabs(x, y))
end
function compensated_multiplication(x::Float64, y::Float64)
m, p = Base.decompose(x)
n, q = Base.decompose(y)
x*y, (UInt64(m)*UInt64(n) & 0x000fffffffffffff)*2.0^(p + q)
end Chaining compensated values is a real pain, it's almost as if one wants a type for that 😸 |
Your I'm not sure I quite understand what's happening in the multiplication routine, since I think the second isn't a correction of the first. If that's your intention, I think it's well-accepted that at this point the best way to achieve that with floating-point is to use the fact that |
It does work though: julia> x, y = 0.1, 0.3
(0.1, 0.3)
julia> compensated_multiplication(x, y)
(0.03, 1.6653345369377347e-18)
julia> big(x)*big(y) - x*y
1.665334536937734749005689281744683170958705837282325806780747257107577752321959e-18 How it works: I guess using |
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.
I'm still a bit confused why we need Veltkamp splitting. What was wrong with the old truncbits
method?
base/twiceprecision.jl
Outdated
Arithmetic", §4.4.1. | ||
""" | ||
function splitprec(x::T) where {T<:AbstractFloat} | ||
xs, xe = frexp(x) |
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.
frexp
/ldexp
is probably not the most efficient way to do this. It might be simpler to just check if its magnitude is above some threshold, and scale it down if necessary
base/float.jl
Outdated
@@ -825,28 +829,7 @@ fpinttype(::Type{Float64}) = UInt64 | |||
fpinttype(::Type{Float32}) = UInt32 |
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.
I'm still not a huge fan of this name: I keep reading this as "f pint type". It seems like there should be a more general name for this (i.e. give me the unsigned integer type with the same size as this type).
We could do typeof(reinterpret(Unsigned, zero(F)))
but that seems a bit silly.
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.
I changed it to uinttype
.
@@ -1,5 +1,7 @@ | |||
# This file is a part of Julia. License is MIT: https://julialang.org/license | |||
|
|||
const IEEEFloat = Union{Float16, Float32, Float64} |
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.
I realise that it is beyond the scope of this PR, but it would be nice if this were an abstract type so I could reuse all this for Float128
once #22649 is fixed.
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.
I just moved this from Base.Math
. I'll leave that kind of change to you 😄. Should be pretty simple, though.
@StefanKarpinski I think that only works if your calculation rounds down (or if you've changed the rounding mode). e.g. try
|
Yeah, might need some sign fiddling in some cases. |
Clever, thanks for explaining. |
We don't really; I think it was left over from an older version that still used some splitting. (With the old Thanks for the review! |
base/float.jl
Outdated
@@ -711,12 +713,12 @@ end | |||
|
|||
Test whether a floating point number is subnormal. | |||
""" | |||
function issubnormal end | |||
function issubnormal(x::T) where {T<:IEEEFloat} | |||
y = reinterpret(uinttype(T), x) |
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.
can use reinterpret(Unsigned, x)
This seems good to go. I'll merge soon barring concerns. |
@test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) | ||
@test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) | ||
@test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) | ||
@test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) |
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.
Never, never any bugs, right? ;) Sorry to be the bearer of bad news, but I imagine you'll be interested in #23497.
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.
By definition, if you think you've seen a bug in this code, you're just imagining things. Really. Honest. No bugs.
Ahhh! (sploosh)
This high-precision arithmetic stuff stinks. But fortunately no one will ever, ever report a bug in this code again. And if they do, someone (probably me) is going to be swimming with the fishes. Got it?