diff --git a/base/float.jl b/base/float.jl index fc4cef09b48ad..aa61f2c8449e3 100644 --- a/base/float.jl +++ b/base/float.jl @@ -882,7 +882,10 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn end end function (::Type{$Ti})(x::$Tf) - if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && isinteger(x) + # When typemax(Ti) is not representable by Tf but typemax(Ti) + 1 is, + # then < Tf(typemax(Ti) + 1) is stricter than <= Tf(typemax(Ti)). Using + # the former causes us to throw on UInt64(Float64(typemax(UInt64))+1) + if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))+one(Tf))) && isinteger(x) return unsafe_trunc($Ti,x) else throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index a0d6e8743514b..268d699684cbb 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -256,3 +256,34 @@ end @test isapprox(typemin(T), 0.0, rtol=1) end end + +@testset "Conversion from floating point to unsigned integer near extremes (#51063)" begin + @test_throws InexactError UInt32(4.2949673f9) + @test_throws InexactError UInt64(1.8446744f19) + @test_throws InexactError UInt64(1.8446744073709552e19) + @test_throws InexactError UInt128(3.402823669209385e38) +end + +@testset "Conversion from floating point to integer near extremes (exhaustive)" begin + for Ti in Base.BitInteger_types, Tf in (Float16, Float32, Float64), x in (typemin(Ti), typemax(Ti)) + y = Tf(x) + for i in -3:3 + z = nextfloat(y, i) + + result = isfinite(z) ? round(BigInt, z) : error + result = result !== error && typemin(Ti) <= result <= typemax(Ti) ? result : error + + if result === error + # @test_throws InexactError round(Ti, z) Broken because of #51113 + @test_throws InexactError Ti(z) + else + @test result == round(Ti, z) + if isinteger(z) + @test result == Ti(z) + else + @test_throws InexactError Ti(z) + end + end + end + end +end