Skip to content

Commit c2da085

Browse files
Round from zero support for non-BigFloats (#41246)
Co-authored-by: Steven G. Johnson <[email protected]> Co-authored-by: Steven G. Johnson <[email protected]> Co-authored-by: Mosè Giordano <[email protected]> Co-authored-by: Dilum Aluthge <[email protected]>
1 parent 8020549 commit c2da085

File tree

7 files changed

+79
-18
lines changed

7 files changed

+79
-18
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Library changes
4444
now be called on a dictionary or set shared by arbitrary tasks provided that there are no
4545
tasks mutating the dictionary or set ([#44534]).
4646
* Predicate function negation `!f` now returns a composed function `(!) ∘ f` instead of an anonymous function ([#44752]).
47+
* `RoundFromZero` now works for non-`BigFloat` types ([#41246]).
4748

4849

4950
Standard library changes

base/div.jl

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ without any intermediate rounding.
1717
1818
See also [`fld`](@ref) and [`cld`](@ref), which are special cases of this function.
1919
20+
!!! compat "Julia 1.9"
21+
`RoundFromZero` requires at least Julia 1.9.
22+
2023
# Examples:
2124
```jldoctest
2225
julia> div(4, 3, RoundDown) # Matches fld(4, 3)
@@ -33,6 +36,10 @@ julia> div(-5, 2, RoundNearestTiesAway)
3336
-3
3437
julia> div(-5, 2, RoundNearestTiesUp)
3538
-2
39+
julia> div(4, 3, RoundFromZero)
40+
2
41+
julia> div(-4, 3, RoundFromZero)
42+
-2
3643
```
3744
"""
3845
div(x, y, r::RoundingMode)
@@ -63,6 +70,13 @@ without any intermediate rounding.
6370
`[0,-y)` otherwise. The result may not be exact if `x` and `y` have the same sign, and
6471
`abs(x) < abs(y)`. See also [`RoundUp`](@ref).
6572
73+
- if `r == RoundFromZero`, then the result is in the interval `(-y, 0]` if `y` is positive, or
74+
`[0, -y)` otherwise. The result may not be exact if `x` and `y` have the same sign, and
75+
`abs(x) < abs(y)`. See also [`RoundFromZero`](@ref).
76+
77+
!!! compat "Julia 1.9"
78+
`RoundFromZero` requires at least Julia 1.9.
79+
6680
# Examples:
6781
```jldoctest
6882
julia> x = 9; y = 4;
@@ -86,6 +100,10 @@ rem(x, y, ::RoundingMode{:Up}) = mod(x, -y)
86100
rem(x, y, r::RoundingMode{:Nearest}) = x - y*div(x, y, r)
87101
rem(x::Integer, y::Integer, r::RoundingMode{:Nearest}) = divrem(x, y, r)[2]
88102

103+
function rem(x, y, ::typeof(RoundFromZero))
104+
signbit(x) == signbit(y) ? rem(x, y, RoundUp) : rem(x, y, RoundDown)
105+
end
106+
89107
"""
90108
fld(x, y)
91109
@@ -240,6 +258,10 @@ function divrem(x::Integer, y::Integer, rnd::typeof(RoundNearestTiesUp))
240258
end
241259
end
242260

261+
function divrem(x, y, ::typeof(RoundFromZero))
262+
signbit(x) == signbit(y) ? divrem(x, y, RoundUp) : divrem(x, y, RoundDown)
263+
end
264+
243265
"""
244266
fldmod(x, y)
245267
@@ -276,12 +298,16 @@ function div(x::Integer, y::Integer, rnd::Union{typeof(RoundNearest),
276298
divrem(x, y, rnd)[1]
277299
end
278300

301+
function div(x::Integer, y::Integer, ::typeof(RoundFromZero))
302+
signbit(x) == signbit(y) ? div(x, y, RoundUp) : div(x, y, RoundDown)
303+
end
304+
279305
# For bootstrapping purposes, we define div for integers directly. Provide the
280306
# generic signature also
281307
div(a::T, b::T, ::typeof(RoundToZero)) where {T<:Union{BitSigned, BitUnsigned64}} = div(a, b)
282308
div(a::Bool, b::Bool, r::RoundingMode) = div(a, b)
283309
# Prevent ambiguities
284-
for rm in (RoundUp, RoundDown, RoundToZero)
310+
for rm in (RoundUp, RoundDown, RoundToZero, RoundFromZero)
285311
@eval div(a::Bool, b::Bool, r::$(typeof(rm))) = div(a, b)
286312
end
287313
function div(x::Bool, y::Bool, rnd::Union{typeof(RoundNearest),

base/floatfuncs.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ function round(x::T, ::RoundingMode{:NearestTiesUp}) where {T <: AbstractFloat}
236236
copysign(floor((x + (T(0.25) - eps(T(0.5)))) + (T(0.25) + eps(T(0.5)))), x)
237237
end
238238

239+
function Base.round(x::AbstractFloat, ::typeof(RoundFromZero))
240+
signbit(x) ? round(x, RoundDown) : round(x, RoundUp)
241+
end
242+
239243
# isapprox: approximate equality of numbers
240244
"""
241245
isapprox(x, y; atol::Real=0, rtol::Real=atol>0 ? 0 : √eps, nans::Bool=false[, norm::Function])

base/rounding.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ Currently supported rounding modes are:
3737
- [`RoundNearestTiesAway`](@ref)
3838
- [`RoundNearestTiesUp`](@ref)
3939
- [`RoundToZero`](@ref)
40-
- [`RoundFromZero`](@ref) ([`BigFloat`](@ref) only)
40+
- [`RoundFromZero`](@ref)
4141
- [`RoundUp`](@ref)
4242
- [`RoundDown`](@ref)
43+
44+
!!! compat "Julia 1.9"
45+
`RoundFromZero` requires at least Julia 1.9. Prior versions support
46+
`RoundFromZero` for `BigFloat`s only.
4347
"""
4448
struct RoundingMode{T} end
4549

@@ -76,15 +80,18 @@ const RoundDown = RoundingMode{:Down}()
7680
RoundFromZero
7781
7882
Rounds away from zero.
79-
This rounding mode may only be used with `T == BigFloat` inputs to [`round`](@ref).
83+
84+
!!! compat "Julia 1.9"
85+
`RoundFromZero` requires at least Julia 1.9. Prior versions support
86+
`RoundFromZero` for `BigFloat`s only.
8087
8188
# Examples
8289
```jldoctest
8390
julia> BigFloat("1.0000000000000001", 5, RoundFromZero)
8491
1.06
8592
```
8693
"""
87-
const RoundFromZero = RoundingMode{:FromZero}() # mpfr only
94+
const RoundFromZero = RoundingMode{:FromZero}()
8895

8996
"""
9097
RoundNearestTiesAway

test/int.jl

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -352,25 +352,28 @@ end
352352
@testset "rounding division" begin
353353
for x = -100:100
354354
for y = 1:100
355-
for rnd in (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp)
355+
for rnd in (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundFromZero)
356356
@test div(x,y,rnd) == round(x/y,rnd)
357357
@test div(x,-y,rnd) == round(x/-y,rnd)
358358
end
359+
@test divrem(x,y,RoundFromZero) == (div(x,y,RoundFromZero), rem(x,y,RoundFromZero))
360+
@test divrem(x,-y,RoundFromZero) == (div(x,-y,RoundFromZero), rem(x,-y,RoundFromZero))
359361
end
360362
end
361-
for (a, b, nearest, away, up) in (
362-
(3, 2, 2, 2, 2),
363-
(5, 3, 2, 2, 2),
364-
(-3, 2, -2, -2, -1),
365-
(5, 2, 2, 3, 3),
366-
(-5, 2, -2, -3, -2),
367-
(-5, 3, -2, -2, -2),
368-
(5, -3, -2, -2, -2))
363+
for (a, b, nearest, away, up, from_zero) in (
364+
(3, 2, 2, 2, 2, 2),
365+
(5, 3, 2, 2, 2, 2),
366+
(-3, 2, -2, -2, -1, -2),
367+
(5, 2, 2, 3, 3, 3),
368+
(-5, 2, -2, -3, -2, -3),
369+
(-5, 3, -2, -2, -2, -2),
370+
(5, -3, -2, -2, -2, -2))
369371
for sign in (+1, -1)
370372
(a, b) = (a*sign, b*sign)
371373
@test div(a, b, RoundNearest) === nearest
372374
@test div(a, b, RoundNearestTiesAway) === away
373375
@test div(a, b, RoundNearestTiesUp) === up
376+
@test div(a, b, RoundFromZero) === from_zero
374377
end
375378
end
376379

@@ -381,7 +384,7 @@ end
381384
@test div(typemax(Int)-2, typemax(Int), RoundNearest) === 1
382385

383386
# Exhaustively test (U)Int8 to catch any overflow-style issues
384-
for r in (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp)
387+
for r in (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundFromZero)
385388
for T in (UInt8, Int8)
386389
for x in typemin(T):typemax(T)
387390
for y in typemin(T):typemax(T)

test/numbers.jl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,29 +2519,34 @@ end
25192519
@test rem(T(1), T(2), RoundNearest) == 1
25202520
@test rem(T(1), T(2), RoundDown) == 1
25212521
@test rem(T(1), T(2), RoundUp) == -1
2522+
@test rem(T(1), T(2), RoundFromZero) == -1
25222523
@test rem(T(1.5), T(2), RoundToZero) == 1.5
25232524
@test rem(T(1.5), T(2), RoundNearest) == -0.5
25242525
@test rem(T(1.5), T(2), RoundDown) == 1.5
25252526
@test rem(T(1.5), T(2), RoundUp) == -0.5
2527+
@test rem(T(1.5), T(2), RoundFromZero) == -0.5
25262528
@test rem(T(-1), T(2), RoundToZero) == -1
25272529
@test rem(T(-1), T(2), RoundNearest) == -1
25282530
@test rem(T(-1), T(2), RoundDown) == 1
25292531
@test rem(T(-1), T(2), RoundUp) == -1
2532+
@test rem(T(-1), T(2), RoundFromZero) == 1
25302533
@test rem(T(-1.5), T(2), RoundToZero) == -1.5
25312534
@test rem(T(-1.5), T(2), RoundNearest) == 0.5
25322535
@test rem(T(-1.5), T(2), RoundDown) == 0.5
25332536
@test rem(T(-1.5), T(2), RoundUp) == -1.5
2534-
for mode in [RoundToZero, RoundNearest, RoundDown, RoundUp]
2537+
@test rem(T(-1.5), T(2), RoundFromZero) == 0.5
2538+
for mode in [RoundToZero, RoundNearest, RoundDown, RoundUp, RoundFromZero]
25352539
@test isnan(rem(T(1), T(0), mode))
25362540
@test isnan(rem(T(Inf), T(2), mode))
25372541
@test isnan(rem(T(1), T(NaN), mode))
25382542
# FIXME: The broken case erroneously returns -Inf
2539-
@test rem(T(4), floatmin(T) * 2, mode) == 0 broken=(T == BigFloat && mode == RoundUp)
2543+
@test rem(T(4), floatmin(T) * 2, mode) == 0 broken=(T == BigFloat && mode in (RoundUp,RoundFromZero))
25402544
end
25412545
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundToZero), -0.0)
25422546
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundNearest), -0.0)
2543-
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundDown), 0.0)
2544-
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundUp), 0.0)
2547+
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundDown), 0.0)
2548+
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundUp), 0.0)
2549+
@test isequal(rem(nextfloat(typemin(T)), T(2), RoundFromZero), 0.0)
25452550
end
25462551

25472552
@testset "rem for $T RoundNearest" for T in (Int8, Int16, Int32, Int64, Int128)

test/rounding.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ end
128128
else
129129
@test u === r
130130
end
131+
132+
r = round(u, RoundFromZero)
133+
if isfinite(u)
134+
@test isfinite(r)
135+
@test isinteger(r)
136+
@test signbit(u) ? (r == floor(u)) : (r == ceil(u))
137+
@test signbit(u) == signbit(r)
138+
else
139+
@test u === r
140+
end
131141
end
132142
end
133143
end
@@ -171,6 +181,7 @@ end
171181
@test round.(y) t[(i+1+isodd(i>>2))>>2 for i in r]
172182
@test broadcast(x -> round(x, RoundNearestTiesAway), y) t[(i+1+(i>=0))>>2 for i in r]
173183
@test broadcast(x -> round(x, RoundNearestTiesUp), y) t[(i+2)>>2 for i in r]
184+
@test broadcast(x -> round(x, RoundFromZero), y) t[(i+3*(i>=0))>>2 for i in r]
174185
end
175186
end
176187
end
@@ -190,6 +201,10 @@ end
190201
@test round(Int,-2.5,RoundNearestTiesUp) == -2
191202
@test round(Int,-1.5,RoundNearestTiesUp) == -1
192203
@test round(Int,-1.9) == -2
204+
@test round(Int,nextfloat(1.0),RoundFromZero) == 2
205+
@test round(Int,-nextfloat(1.0),RoundFromZero) == -2
206+
@test round(Int,prevfloat(1.0),RoundFromZero) == 1
207+
@test round(Int,-prevfloat(1.0),RoundFromZero) == -1
193208
@test_throws InexactError round(Int64, 9.223372036854776e18)
194209
@test round(Int64, 9.223372036854775e18) == 9223372036854774784
195210
@test_throws InexactError round(Int64, -9.223372036854778e18)

0 commit comments

Comments
 (0)