Skip to content

Commit 7617e4b

Browse files
LilithHafnermikmoore
authored andcommitted
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]> (cherry picked from commit e7a1def)
1 parent 4f7a801 commit 7617e4b

File tree

4 files changed

+40
-3
lines changed

4 files changed

+40
-3
lines changed

base/essentials.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,9 @@ Stacktrace:
397397
[...]
398398
```
399399
400-
If `T` is a [`AbstractFloat`](@ref) type,
401-
then it will return the closest value to `x` representable by `T`.
400+
If `T` is a [`AbstractFloat`](@ref) type, then it will return the
401+
closest value to `x` representable by `T`. Inf is treated as one
402+
ulp greater than `floatmax(T)` for purposes of determining nearest.
402403
403404
```jldoctest
404405
julia> x = 1/3

base/float.jl

Lines changed: 13 additions & 0 deletions
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

Lines changed: 12 additions & 1 deletion
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

Lines changed: 12 additions & 0 deletions
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)