Skip to content

Commit ff9cebe

Browse files
committed
Merge pull request #9344 from simonbyrne/roundeven
Changes behaviour of round to use default rounding mode
2 parents 20a5c3d + 2999af2 commit ff9cebe

File tree

14 files changed

+301
-121
lines changed

14 files changed

+301
-121
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ Language changes
5454
* `Char` + `Int` = `Char`
5555
* `Char` - `Char` = `Int`
5656

57+
* `round` rounds to the nearest integer using the default rounding mode,
58+
which is ties to even by default ([#8750]).
59+
5760
Compiler improvements
5861
---------------------
5962

base/exports.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ export
8585
RoundDown,
8686
RoundingMode,
8787
RoundNearest,
88+
RoundNearestTiesAway,
89+
RoundNearestTiesUp,
8890
RoundToZero,
8991
RoundUp,
9092
Set,

base/float.jl

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,17 @@ floor{T<:Integer}(::Type{T}, x::FloatingPoint) = trunc(T,floor(x))
166166
ceil {T<:Integer}(::Type{T}, x::FloatingPoint) = trunc(T,ceil(x))
167167
round {T<:Integer}(::Type{T}, x::FloatingPoint) = trunc(T,round(x))
168168

169+
trunc(x::Float64) = box(Float64,trunc_llvm(unbox(Float64,x)))
170+
trunc(x::Float32) = box(Float32,trunc_llvm(unbox(Float32,x)))
169171

170-
# this is needed very early because it is used by Range and colon
171-
function round(x::Float64)
172-
y = trunc(x)
173-
ifelse(x==y,y,trunc(2.0*x-y))
174-
end
175172
floor(x::Float64) = box(Float64,floor_llvm(unbox(Float64,x)))
173+
floor(x::Float32) = box(Float32,floor_llvm(unbox(Float32,x)))
174+
175+
ceil(x::Float64) = box(Float64,ceil_llvm(unbox(Float64,x)))
176+
ceil(x::Float32) = box(Float32,ceil_llvm(unbox(Float32,x)))
177+
178+
round(x::Float64) = box(Float64,rint_llvm(unbox(Float64,x)))
179+
round(x::Float32) = box(Float32,rint_llvm(unbox(Float32,x)))
176180

177181
## floating point promotions ##
178182
promote_rule(::Type{Float32}, ::Type{Float16}) = Float32
@@ -347,13 +351,6 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn
347351
end
348352
end
349353

350-
# adding prevfloat(0.5) will prevent prevfloat(0.5) and odd x with eps(x)=1.0
351-
# from rounding in the wrong direction in RoundToNearest
352-
for Tf in (Float32,Float64)
353-
@eval function round{T<:Integer}(::Type{T}, x::$Tf)
354-
trunc(T,x+copysign($(prevfloat(Tf(0.5))),x))
355-
end
356-
end
357354

358355

359356
@eval begin

base/floatfuncs.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@ end
4343
@vectorize_1arg Number isinf
4444
@vectorize_1arg Number isfinite
4545

46+
47+
round(x::Real, ::RoundingMode{:ToZero}) = trunc(x)
48+
round(x::Real, ::RoundingMode{:Up}) = ceil(x)
49+
round(x::Real, ::RoundingMode{:Down}) = floor(x)
50+
# C-style round
51+
function round(x::FloatingPoint, ::RoundingMode{:NearestTiesAway})
52+
y = trunc(x)
53+
ifelse(x==y,y,trunc(2*x-y))
54+
end
55+
# Java-style round
56+
function round(x::FloatingPoint, ::RoundingMode{:NearestTiesUp})
57+
y = floor(x)
58+
ifelse(x==y,y,copysign(floor(2*x-y),x))
59+
end
60+
round{T<:Integer}(::Type{T}, x::FloatingPoint, r::RoundingMode) = trunc(T,round(x,r))
61+
62+
@vectorize_1arg Real trunc
63+
@vectorize_1arg Real floor
64+
@vectorize_1arg Real ceil
65+
@vectorize_1arg Real round
66+
4667
for f in (:trunc,:floor,:ceil,:round)
4768
@eval begin
4869
function ($f){T,R<:Real}(::Type{T}, x::AbstractArray{R,1})
@@ -57,6 +78,25 @@ for f in (:trunc,:floor,:ceil,:round)
5778
end
5879
end
5980

81+
function round{R<:Real}(x::AbstractArray{R,1}, r::RoundingMode)
82+
[ round(x[i], r) for i = 1:length(x) ]
83+
end
84+
function round{R<:Real}(x::AbstractArray{R,2}, r::RoundingMode)
85+
[ round(x[i,j], r) for i = 1:size(x,1), j = 1:size(x,2) ]
86+
end
87+
function round{R<:Real}(x::AbstractArray{R}, r::RoundingMode)
88+
reshape([ round(x[i], r) for i = 1:length(x) ], size(x))
89+
end
90+
91+
function round{T,R<:Real}(::Type{T}, x::AbstractArray{R,1}, r::RoundingMode)
92+
[ round(T, x[i], r) for i = 1:length(x) ]
93+
end
94+
function round{T,R<:Real}(::Type{T}, x::AbstractArray{R,2}, r::RoundingMode)
95+
[ round(T, x[i,j], r) for i = 1:size(x,1), j = 1:size(x,2) ]
96+
end
97+
function round{T,R<:Real}(::Type{T}, x::AbstractArray{R}, r::RoundingMode)
98+
reshape([ round(T, x[i], r) for i = 1:length(x) ], size(x))
99+
end
60100

61101
# adapted from Matlab File Exchange roundsd: http://www.mathworks.com/matlabcentral/fileexchange/26212
62102
# for round, og is the power of 10 relative to the decimal point

base/math.jl

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export sin, cos, tan, sinh, cosh, tanh, asin, acos, atan,
99
rad2deg, deg2rad,
1010
log, log2, log10, log1p, exponent, exp, exp2, exp10, expm1,
1111
cbrt, sqrt, erf, erfc, erfcx, erfi, dawson,
12-
ceil, floor, trunc, round, significand,
12+
significand,
1313
lgamma, hypot, gamma, lfact, max, min, minmax, ldexp, frexp,
1414
clamp, modf, ^, mod2pi,
1515
airy, airyai, airyprime, airyaiprime, airybi, airybiprime, airyx,
@@ -22,10 +22,10 @@ export sin, cos, tan, sinh, cosh, tanh, asin, acos, atan,
2222

2323
import Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin,
2424
acos, atan, asinh, acosh, atanh, sqrt, log2, log10,
25-
max, min, minmax, ceil, floor, trunc, round, ^, exp2,
25+
max, min, minmax, ^, exp2,
2626
exp10, expm1, log1p
2727

28-
import Core.Intrinsics: nan_dom_err, ceil_llvm, floor_llvm, trunc_llvm, sqrt_llvm, box, unbox, powi_llvm
28+
import Core.Intrinsics: nan_dom_err, sqrt_llvm, box, unbox, powi_llvm
2929

3030
# non-type specific math functions
3131

@@ -132,30 +132,15 @@ sqrt(x::Float32) = box(Float32,sqrt_llvm(unbox(Float32,x)))
132132
sqrt(x::Real) = sqrt(float(x))
133133
@vectorize_1arg Number sqrt
134134

135-
ceil(x::Float64) = box(Float64,ceil_llvm(unbox(Float64,x)))
136-
ceil(x::Float32) = box(Float32,ceil_llvm(unbox(Float32,x)))
137-
@vectorize_1arg Real ceil
138135

139-
trunc(x::Float64) = box(Float64,trunc_llvm(unbox(Float64,x)))
140-
trunc(x::Float32) = box(Float32,trunc_llvm(unbox(Float32,x)))
141-
@vectorize_1arg Real trunc
142-
143-
for f in (:significand, :rint) # :nearbyint
136+
for f in (:significand,)
144137
@eval begin
145138
($f)(x::Float64) = ccall(($(string(f)),libm), Float64, (Float64,), x)
146139
($f)(x::Float32) = ccall(($(string(f,"f")),libm), Float32, (Float32,), x)
147140
@vectorize_1arg Real $f
148141
end
149142
end
150143

151-
function round(x::Float32)
152-
y = trunc(x)
153-
ifelse(x==y,y,trunc(2.f0*x-y))
154-
end
155-
@vectorize_1arg Real round
156-
157-
floor(x::Float32) = box(Float32,floor_llvm(unbox(Float32,x)))
158-
@vectorize_1arg Real floor
159144

160145
hypot(x::Real, y::Real) = hypot(promote(float(x), float(y))...)
161146
function hypot{T<:FloatingPoint}(x::T, y::T)

base/mpfr.jl

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Base.GMP: ClongMax, CulongMax, CdoubleMax
2626

2727
import Base.Math.lgamma_r
2828

29-
const ROUNDING_MODE = [0]
29+
const ROUNDING_MODE = Cint[0]
3030
const DEFAULT_PRECISION = [256]
3131

3232
# Basic type and initialization definitions
@@ -93,29 +93,32 @@ convert(::Type{BigFloat}, x::Real) = BigFloat(x)
9393
convert(::Type{FloatingPoint}, x::BigInt) = BigFloat(x)
9494

9595
## BigFloat -> Integer
96-
function unsafe_cast(::Type{Int64}, x::BigFloat, r::RoundingMode)
96+
function unsafe_cast(::Type{Int64}, x::BigFloat, ri::Cint)
9797
ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t,
98-
(Ptr{BigFloat}, Int32), &x, to_mpfr(r))
98+
(Ptr{BigFloat}, Cint), &x, ri)
9999
end
100-
function unsafe_cast(::Type{UInt64}, x::BigFloat, r::RoundingMode)
100+
function unsafe_cast(::Type{UInt64}, x::BigFloat, ri::Cint)
101101
ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t,
102-
(Ptr{BigFloat}, Int32), &x, to_mpfr(r))
102+
(Ptr{BigFloat}, Cint), &x, ri)
103103
end
104104

105-
function unsafe_cast{T<:Signed}(::Type{T}, x::BigFloat, r::RoundingMode)
106-
unsafe_cast(Int64, x, r) % T
105+
function unsafe_cast{T<:Signed}(::Type{T}, x::BigFloat, ri::Cint)
106+
unsafe_cast(Int64, x, ri) % T
107107
end
108-
function unsafe_cast{T<:Unsigned}(::Type{T}, x::BigFloat, r::RoundingMode)
109-
unsafe_cast(UInt64, x, r) % T
108+
function unsafe_cast{T<:Unsigned}(::Type{T}, x::BigFloat, ri::Cint)
109+
unsafe_cast(UInt64, x, ri) % T
110110
end
111111

112-
function unsafe_cast(::Type{BigInt}, x::BigFloat, r::RoundingMode)
112+
function unsafe_cast(::Type{BigInt}, x::BigFloat, ri::Cint)
113113
# actually safe, just keep naming consistent
114114
z = BigInt()
115115
ccall((:mpfr_get_z, :libmpfr), Int32, (Ptr{BigInt}, Ptr{BigFloat}, Int32),
116-
&z, &x, to_mpfr(r))
116+
&z, &x, ri)
117117
z
118118
end
119+
unsafe_cast(::Type{Int128}, x::BigFloat, ri::Cint) = Int128(unsafe_cast(BigInt,x,ri))
120+
unsafe_cast(::Type{UInt128}, x::BigFloat, ri::Cint) = UInt128(unsafe_cast(BigInt,x,ri))
121+
unsafe_cast{T<:Integer}(::Type{T}, x::BigFloat, r::RoundingMode) = unsafe_cast(T,x,to_mpfr(r))
119122

120123
unsafe_trunc{T<:Integer}(::Type{T}, x::BigFloat) = unsafe_cast(T,x,RoundToZero)
121124

@@ -132,21 +135,21 @@ function ceil{T<:Union(Signed,Unsigned)}(::Type{T}, x::BigFloat)
132135
unsafe_cast(T,x,RoundUp)
133136
end
134137

138+
function round{T<:Union(Signed,Unsigned)}(::Type{T}, x::BigFloat)
139+
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
140+
unsafe_cast(T,x,ROUNDING_MODE[end])
141+
end
142+
135143
trunc(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundToZero)
136144
floor(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundDown)
137145
ceil(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundUp)
146+
round(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, ROUNDING_MODE[end])
147+
138148
# convert/round/trunc/floor/ceil(Integer, x) should return a BigInt
139149
trunc(::Type{Integer}, x::BigFloat) = trunc(BigInt, x)
140150
floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x)
141151
ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x)
142-
143-
for Ti in (Int128,UInt128)
144-
@eval begin
145-
trunc(::Type{$Ti}, x::BigFloat) = ($Ti)(trunc(BigInt, x))
146-
floor(::Type{$Ti}, x::BigFloat) = ($Ti)(floor(BigInt, x))
147-
ceil(::Type{$Ti}, x::BigFloat) = ($Ti)(ceil(BigInt, x))
148-
end
149-
end
152+
round(::Type{Integer}, x::BigFloat) = round(BigInt, x)
150153

151154
convert(::Type{Bool}, x::BigFloat) = (x != 0)
152155
function convert(::Type{BigInt},x::BigFloat)
@@ -625,11 +628,11 @@ end
625628
maxintfloat(x::BigFloat) = BigFloat(2)^precision(x)
626629
maxintfloat(::Type{BigFloat}) = BigFloat(2)^get_bigfloat_precision()
627630

628-
to_mpfr(::RoundingMode{:TiesToEven}) = 0
629-
to_mpfr(::RoundingMode{:TowardZero}) = 1
630-
to_mpfr(::RoundingMode{:TowardPositive}) = 2
631-
to_mpfr(::RoundingMode{:TowardNegative}) = 3
632-
to_mpfr(::RoundingMode{:AwayFromZero}) = 4
631+
to_mpfr(::RoundingMode{:Nearest}) = Cint(0)
632+
to_mpfr(::RoundingMode{:ToZero}) = Cint(1)
633+
to_mpfr(::RoundingMode{:Up}) = Cint(2)
634+
to_mpfr(::RoundingMode{:Down}) = Cint(3)
635+
to_mpfr(::RoundingMode{:FromZero}) = Cint(4)
633636

634637
function from_mpfr(c::Integer)
635638
if c == 0
@@ -687,7 +690,7 @@ function isinteger(x::BigFloat)
687690
return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0
688691
end
689692

690-
for f in (:ceil, :floor, :trunc, :round)
693+
for f in (:ceil, :floor, :trunc)
691694
@eval begin
692695
function ($f)(x::BigFloat)
693696
z = BigFloat()
@@ -697,6 +700,17 @@ for f in (:ceil, :floor, :trunc, :round)
697700
end
698701
end
699702

703+
function round(x::BigFloat)
704+
z = BigFloat()
705+
ccall((:mpfr_rint, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cint), &z, &x, ROUNDING_MODE[end])
706+
return z
707+
end
708+
function round(x::BigFloat,::RoundingMode{:NearestTiesAway})
709+
z = BigFloat()
710+
ccall((:mpfr_round, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &x)
711+
return z
712+
end
713+
700714
function isinf(x::BigFloat)
701715
return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0
702716
end

base/printf.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ function ini_hex(x::SmallFloatingPoint, n::Int, symbols::Array{UInt8,1})
909909
else
910910
s, p = frexp(x)
911911
sigbits = 4*min(n-1,13)
912-
s = 0.25*Base.Math.rint(ldexp(s,1+sigbits))
912+
s = 0.25*round(ldexp(s,1+sigbits))
913913
# ensure last 2 exponent bits either 01 or 10
914914
u = (reinterpret(UInt64,s) & 0x003f_ffff_ffff_ffff) >> (52-sigbits)
915915
if n > 14

base/rational.jl

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,25 @@ for op in (:div, :fld, :cld)
220220
end
221221
end
222222

223-
trunc{T<:Integer}(::Type{T}, x::Rational) = convert(T,div(x.num,x.den))
224-
floor{T<:Integer}(::Type{T}, x::Rational) = convert(T,fld(x.num,x.den))
225-
ceil {T<:Integer}(::Type{T}, x::Rational) = convert(T,cld(x.num,x.den))
226-
function round{T<:Integer}(::Type{T}, x::Rational)
227-
t = trunc(T,x)
228-
r = x-t
229-
abs(r.num) > (r.den-one(r.den))>>1 ? t + copysign(one(t),x) : t
223+
trunc{T}(::Type{T}, x::Rational) = convert(T,div(x.num,x.den))
224+
floor{T}(::Type{T}, x::Rational) = convert(T,fld(x.num,x.den))
225+
ceil {T}(::Type{T}, x::Rational) = convert(T,cld(x.num,x.den))
226+
227+
function round{T}(::Type{T}, x::Rational, ::RoundingMode{:Nearest})
228+
q,r = divrem(x.num,x.den)
229+
s = abs(r) < (x.den+one(x.den)+iseven(q))>>1 ? q : q+copysign(one(q),x.num)
230+
convert(T,s)
231+
end
232+
round{T}(::Type{T}, x::Rational) = round(T,x,RoundNearest)
233+
function round{T}(::Type{T}, x::Rational, ::RoundingMode{:NearestTiesAway})
234+
q,r = divrem(x.num,x.den)
235+
s = abs(r) < (x.den+one(x.den))>>1 ? q : q+copysign(one(q),x.num)
236+
convert(T,s)
237+
end
238+
function round{T}(::Type{T}, x::Rational, ::RoundingMode{:NearestTiesUp})
239+
q,r = divrem(x.num,x.den)
240+
s = abs(r) < (x.den+one(x.den)+(x.num<0))>>1 ? q : q+copysign(one(q),x.num)
241+
convert(T,s)
230242
end
231243

232244
trunc{T}(x::Rational{T}) = Rational(trunc(T,x))

base/rounding.jl

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@ include("fenv_constants.jl")
33

44
export
55
RoundingMode, RoundNearest, RoundToZero, RoundUp, RoundDown, RoundFromZero,
6+
RoundNearestTiesAway, RoundNearestTiesUp,
67
get_rounding, set_rounding, with_rounding
78

89
## rounding modes ##
910
immutable RoundingMode{T} end
1011

11-
const RoundNearest = RoundingMode{:TiesToEven}()
12-
# const RoundNearestTiesAway = RoundingMode{:TiesToAway}() # currently unsupported
13-
const RoundToZero = RoundingMode{:TowardZero}()
14-
const RoundUp = RoundingMode{:TowardPositive}()
15-
const RoundDown = RoundingMode{:TowardNegative}()
16-
const RoundFromZero = RoundingMode{:AwayFromZero}() # mpfr only
12+
const RoundNearest = RoundingMode{:Nearest}()
13+
const RoundToZero = RoundingMode{:ToZero}()
14+
const RoundUp = RoundingMode{:Up}()
15+
const RoundDown = RoundingMode{:Down}()
16+
const RoundFromZero = RoundingMode{:FromZero}() # mpfr only
17+
# C-style round behaviour
18+
const RoundNearestTiesAway = RoundingMode{:NearestTiesAway}()
19+
# Java-style round behaviour
20+
const RoundNearestTiesUp = RoundingMode{:NearestTiesUp}()
1721

18-
to_fenv(::RoundingMode{:TiesToEven}) = JL_FE_TONEAREST
19-
to_fenv(::RoundingMode{:TowardZero}) = JL_FE_TOWARDZERO
20-
to_fenv(::RoundingMode{:TowardPositive}) = JL_FE_UPWARD
21-
to_fenv(::RoundingMode{:TowardNegative}) = JL_FE_DOWNWARD
22+
to_fenv(::RoundingMode{:Nearest}) = JL_FE_TONEAREST
23+
to_fenv(::RoundingMode{:ToZero}) = JL_FE_TOWARDZERO
24+
to_fenv(::RoundingMode{:Up}) = JL_FE_UPWARD
25+
to_fenv(::RoundingMode{:Down}) = JL_FE_DOWNWARD
2226

2327
function from_fenv(r::Integer)
2428
if r == JL_FE_TONEAREST
@@ -61,16 +65,16 @@ end
6165
# To avoid ambiguous dispatch with methods in mpfr.jl:
6266
call{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode) = _convert_rounding(T,x,r)
6367

64-
_convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:TiesToEven}) = convert(T,x)
65-
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:TowardNegative})
68+
_convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:Nearest}) = convert(T,x)
69+
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:Down})
6670
y = convert(T,x)
6771
y > x ? prevfloat(y) : y
6872
end
69-
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:TowardPositive})
73+
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:Up})
7074
y = convert(T,x)
7175
y < x ? nextfloat(y) : y
7276
end
73-
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:TowardZero})
77+
function _convert_rounding{T<:FloatingPoint}(::Type{T},x::Real,r::RoundingMode{:ToZero})
7478
y = convert(T,x)
7579
if x > 0.0
7680
y > x ? prevfloat(y) : y

0 commit comments

Comments
 (0)