Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ function (::Type{T})(x::Rational{S}) where T<:AbstractFloat where S
P = promote_type(T,S)
convert(T, convert(P,x.num)/convert(P,x.den))::T
end
# avoid spurious overflow (#52394). (Needed for UInt16 or larger;
# we also include Int16 for consistency of accuracy.)
Float16(x::Rational{<:Union{Int16,Int32,Int64,UInt16,UInt32,UInt64}}) =
Float16(Float32(x))
Float16(x::Rational{<:Union{Int128,UInt128}}) =
Float16(Float64(x)) # UInt128 overflows Float32, include Int128 for consistency
Float32(x::Rational{<:Union{Int128,UInt128}}) =
Float32(Float64(x)) # UInt128 overflows Float32, include Int128 for consistency
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't these suffer from double-rounding accuracy loss? @oscardssmith

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they would. Doing this properly is fairly hard though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also worth noting that Float16(num)/Float16(den) also potentially has triple rounding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question is: is this problem worse than the problem this PR fixes? I.e. should we merge and improve this later, or should this wait until this PR does it "right" (whatever that means?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should merge and improve later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my thought as well, but want to confirm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would doing it "properly" be something like:

a = Float64(x)
b = Float32(a)
# check if we double-rounded in the wrong direction
if x > a && b < a && nextfloat(b) < a
    b = nextfloat(b)
elseif x < a && b > a && prevfloat(b) > a
    b = prevfloat(b)
end
return b

Copy link
Member Author

@stevengj stevengj Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A example of a test case where double rounding gives a different result is:

julia> r = 12928845309018111//18014398509481984
12928845309018111//18014398509481984

julia> Float32(r) == Float32(Float64(r))
false

@vtjnash's code doesn't fix this, however — its output matches Float32(Float64(r)) on this example.

(Example constructed by tweaking a number almost exactly halfway between two Float32 values.)


function Rational{T}(x::AbstractFloat) where T<:Integer
r = rationalize(T, x, tol=0)
Expand Down
5 changes: 5 additions & 0 deletions test/float16.jl
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ const minsubf16_32 = Float32(minsubf16)
# issues #33076
@test Float16(1f5) == Inf16

# issue #52394
@test Float16(10^8 // (10^9 + 1)) == convert(Float16, 10^8 // (10^9 + 1)) == Float16(0.1)
@test Float16((typemax(UInt128)-0x01) // typemax(UInt128)) == Float16(1.0)
@test Float32((typemax(UInt128)-0x01) // typemax(UInt128)) == Float32(1.0)

@testset "conversion to Float16 from" begin
for T in (Float32, Float64, BigFloat)
@testset "conversion from $T" begin
Expand Down