Skip to content

Commit e7a1def

Browse files
Fix bug when rounding large numbers to floating point types (#54314)
- fix #52355 using option 4 (round to nearest representable integer) - update docstrings *including documenting convert to Inf behavior even though Inf is not the "closest" floating point value* - add some assorted tests --------- Co-authored-by: mikmoore <[email protected]>
1 parent a946631 commit e7a1def

File tree

4 files changed

+40
-3
lines changed

4 files changed

+40
-3
lines changed

base/essentials.jl

+3-2
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,9 @@ Stacktrace:
399399
[...]
400400
```
401401
402-
If `T` is a [`AbstractFloat`](@ref) type,
403-
then it will return the closest value to `x` representable by `T`.
402+
If `T` is a [`AbstractFloat`](@ref) type, then it will return the
403+
closest value to `x` representable by `T`. Inf is treated as one
404+
ulp greater than `floatmax(T)` for purposes of determining nearest.
404405
405406
```jldoctest
406407
julia> x = 1/3

base/float.jl

+13
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,19 @@ round(x::IEEEFloat, ::RoundingMode{:Down}) = floor_llvm(x)
464464
round(x::IEEEFloat, ::RoundingMode{:Up}) = ceil_llvm(x)
465465
round(x::IEEEFloat, ::RoundingMode{:Nearest}) = rint_llvm(x)
466466

467+
rounds_up(x, ::RoundingMode{:Down}) = false
468+
rounds_up(x, ::RoundingMode{:Up}) = true
469+
rounds_up(x, ::RoundingMode{:ToZero}) = signbit(x)
470+
rounds_up(x, ::RoundingMode{:FromZero}) = !signbit(x)
471+
function _round_convert(::Type{T}, x_integer, x, r::Union{RoundingMode{:ToZero}, RoundingMode{:FromZero}, RoundingMode{:Up}, RoundingMode{:Down}}) where {T<:AbstractFloat}
472+
x_t = convert(T, x_integer)
473+
if rounds_up(x, r)
474+
x_t < x ? nextfloat(x_t) : x_t
475+
else
476+
x_t > x ? prevfloat(x_t) : x_t
477+
end
478+
end
479+
467480
## floating point promotions ##
468481
promote_rule(::Type{Float32}, ::Type{Float16}) = Float32
469482
promote_rule(::Type{Float64}, ::Type{Float16}) = Float64

base/rounding.jl

+12-1
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ The [`RoundingMode`](@ref) `r` controls the direction of the rounding; the defau
338338
of 0.5) being rounded to the nearest even integer. Note that `round` may give incorrect
339339
results if the global rounding mode is changed (see [`rounding`](@ref)).
340340
341+
When rounding to a floating point type, will round to integers representable by that type
342+
(and Inf) rather than true integers. Inf is treated as one ulp greater than the
343+
`floatmax(T)` for purposes of determining "nearest", similar to [`convert`](@ref).
344+
341345
# Examples
342346
```jldoctest
343347
julia> round(1.7)
@@ -363,6 +367,12 @@ julia> round(123.456; sigdigits=2)
363367
364368
julia> round(357.913; sigdigits=4, base=2)
365369
352.0
370+
371+
julia> round(Float16, typemax(UInt128))
372+
Inf16
373+
374+
julia> floor(Float16, typemax(UInt128))
375+
Float16(6.55e4)
366376
```
367377
368378
!!! note
@@ -466,6 +476,7 @@ floor(::Type{T}, x) where T = round(T, x, RoundDown)
466476
ceil(::Type{T}, x) where T = round(T, x, RoundUp)
467477
round(::Type{T}, x) where T = round(T, x, RoundNearest)
468478

469-
round(::Type{T}, x, r::RoundingMode) where T = convert(T, round(x, r))
479+
round(::Type{T}, x, r::RoundingMode) where T = _round_convert(T, round(x, r), x, r)
480+
_round_convert(::Type{T}, x_integer, x, r) where T = convert(T, x_integer)
470481

471482
round(x::Integer, r::RoundingMode) = x

test/rounding.jl

+12
Original file line numberDiff line numberDiff line change
@@ -458,3 +458,15 @@ end
458458
@test_throws InexactError round(Int128, -Inf16)
459459
# More comprehensive testing is present in test/floatfuncs.jl
460460
end
461+
462+
@testset "floor(<:AbstractFloat, large_number) (#52355)" begin
463+
@test floor(Float32, 0xffff_ffff) == prevfloat(2f0^32) <= 0xffff_ffff
464+
@test trunc(Float16, typemax(UInt128)) == floatmax(Float16)
465+
@test round(Float16, typemax(UInt128)) == Inf16
466+
for i in [-BigInt(floatmax(Float64)), -BigInt(floatmax(Float64))*100, BigInt(floatmax(Float64)), BigInt(floatmax(Float64))*100]
467+
f = ceil(Float64, i)
468+
@test f >= i
469+
@test isinteger(f) || isinf(f)
470+
@test prevfloat(f) < i
471+
end
472+
end

0 commit comments

Comments
 (0)