diff --git a/base/float.jl b/base/float.jl index 716dfa4d8a398..6b468f238279d 100644 --- a/base/float.jl +++ b/base/float.jl @@ -348,26 +348,26 @@ trunc(::Type{Integer}, x::Float64) = trunc(Int,x) trunc(::Type{T}, x::Float16) where {T<:Integer} = trunc(T, Float32(x)) # fallbacks -floor(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,_round(x, RoundDown)) +floor(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundDown)) floor(::Type{T}, x::Float16) where {T<:Integer} = floor(T, Float32(x)) -ceil(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,_round(x, RoundUp)) +ceil(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundUp)) ceil(::Type{T}, x::Float16) where {T<:Integer} = ceil(T, Float32(x)) -round(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,_round(x, RoundNearest)) +round(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundNearest)) round(::Type{T}, x::Float16) where {T<:Integer} = round(T, Float32(x)) -_round(x::Float64, r::RoundingMode{:ToZero}) = trunc_llvm(x) -_round(x::Float32, r::RoundingMode{:ToZero}) = trunc_llvm(x) -_round(x::Float64, r::RoundingMode{:Down}) = floor_llvm(x) -_round(x::Float32, r::RoundingMode{:Down}) = floor_llvm(x) -_round(x::Float64, r::RoundingMode{:Up}) = ceil_llvm(x) -_round(x::Float32, r::RoundingMode{:Up}) = ceil_llvm(x) -_round(x::Float64, r::RoundingMode{:Nearest}) = rint_llvm(x) -_round(x::Float32, r::RoundingMode{:Nearest}) = rint_llvm(x) +round(x::Float64, r::RoundingMode{:ToZero}) = trunc_llvm(x) +round(x::Float32, r::RoundingMode{:ToZero}) = trunc_llvm(x) +round(x::Float64, r::RoundingMode{:Down}) = floor_llvm(x) +round(x::Float32, r::RoundingMode{:Down}) = floor_llvm(x) +round(x::Float64, r::RoundingMode{:Up}) = ceil_llvm(x) +round(x::Float32, r::RoundingMode{:Up}) = ceil_llvm(x) +round(x::Float64, r::RoundingMode{:Nearest}) = rint_llvm(x) +round(x::Float32, r::RoundingMode{:Nearest}) = rint_llvm(x) -_round(x::Float16, r::RoundingMode{:ToZero}) = Float16(_round(Float32(x), r)) -_round(x::Float16, r::RoundingMode{:Down}) = Float16(_round(Float32(x), r)) -_round(x::Float16, r::RoundingMode{:Up}) = Float16(_round(Float32(x), r)) -_round(x::Float16, r::RoundingMode{:Nearest}) = Float16(_round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:ToZero}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Down}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Up}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Nearest}) = Float16(round(Float32(x), r)) ## floating point promotions ## promote_rule(::Type{Float32}, ::Type{Float16}) = Float32 @@ -660,7 +660,7 @@ 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)))) && (_round(x, RoundToZero) == x) + if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && (round(x, RoundToZero) == x) return unsafe_trunc($Ti,x) else throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) @@ -681,7 +681,7 @@ 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)))) && (_round(x, RoundToZero) == x) + if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))) && (round(x, RoundToZero) == x) return unsafe_trunc($Ti,x) else throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 85db2a2ae92e3..0a55ac6342578 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -114,13 +114,14 @@ julia> round(357.913; sigdigits=4, base=2) # Extensions -To extend `round` to new numeric types, it is typically sufficient to define `Base._round(x::NewType, ::RoundingMode)`. +To extend `round` to new numeric types, it is typically sufficient to define `Base.round(x::NewType, r::RoundingMode)`. """ round(T::Type, x) round(::Type{T}, x::AbstractFloat, r::RoundingMode{:ToZero}) where {T<:Integer} = trunc(T, x) -round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T, _round(x,r)) +round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T, round(x,r)) +# NOTE: this relies on the current keyword dispatch behaviour (#9498). function round(x::Real, r::RoundingMode=RoundNearest; digits::Union{Nothing,Integer}=nothing, sigdigits::Union{Nothing,Integer}=nothing, base=10) isfinite(x) || return x @@ -130,12 +131,15 @@ trunc(x::Real; kwargs...) = round(x, RoundToZero; kwargs...) floor(x::Real; kwargs...) = round(x, RoundDown; kwargs...) ceil(x::Real; kwargs...) = round(x, RoundUp; kwargs...) -_round(x, r::RoundingMode, digits::Nothing, sigdigits::Nothing, base) = _round(x, r) -_round(x::Integer, r::RoundingMode) = x +# avoid recursive calls +round(x::Real, r::RoundingMode) = throw(MethodError(round, (x,r))) +round(x::Integer, r::RoundingMode) = x + +_round(x, r::RoundingMode, digits::Nothing, sigdigits::Nothing, base) = round(x, r) # round x to multiples of 1/invstep function _round_invstep(x, invstep, r::RoundingMode) - y = _round(x * invstep, r) / invstep + y = round(x * invstep, r) / invstep if !isfinite(y) return x end @@ -145,7 +149,7 @@ end # round x to multiples of step function _round_step(x, step, r::RoundingMode) # TODO: use div with rounding mode - y = _round(x / step, r) * step + y = round(x / step, r) * step if !isfinite(y) if x > 0 return (r == RoundUp ? oftype(x, Inf) : zero(x)) @@ -191,12 +195,12 @@ _round(x, r::RoundingMode, digits::Integer, sigdigits::Integer, base) = throw(ArgumentError("`round` cannot use both `digits` and `sigdigits` arguments.")) # C-style round -function _round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway}) +function round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway}) y = trunc(x) ifelse(x==y,y,trunc(2*x-y)) end # Java-style round -function _round(x::AbstractFloat, ::RoundingMode{:NearestTiesUp}) +function round(x::AbstractFloat, ::RoundingMode{:NearestTiesUp}) y = floor(x) ifelse(x==y,y,copysign(floor(2*x-y),x)) end diff --git a/base/irrationals.jl b/base/irrationals.jl index 5b4ab8351996c..115e37d856acb 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -125,7 +125,7 @@ for op in Symbol[:+, :-, :*, :/, :^] end *(x::Bool, y::AbstractIrrational) = ifelse(x, Float64(y), 0.0) -_round(x::Irrational, r::RoundingMode) = _round(float(x), r) +round(x::Irrational, r::RoundingMode) = round(float(x), r) macro irrational(sym, val, def) esym = esc(sym) diff --git a/base/mpfr.jl b/base/mpfr.jl index e442838275e0e..f3b68d35bf421 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -827,9 +827,13 @@ function isinteger(x::BigFloat) return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end -for f in (:ceil, :floor, :trunc) +for (f,R) in ((:roundeven, :Nearest), + (:ceil, :Up), + (:floor, :Down), + (:trunc, :ToZero), + (:round, :NearestTiesAway)) @eval begin - function ($f)(x::BigFloat) + function round(x::BigFloat, ::RoundingMode{$(QuoteNode(R))}) z = BigFloat() ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z @@ -837,17 +841,6 @@ for f in (:ceil, :floor, :trunc) end end -function round(x::BigFloat) - z = BigFloat() - ccall((:mpfr_rint, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cint), z, x, ROUNDING_MODE[]) - return z -end -function round(x::BigFloat,::RoundingMode{:NearestTiesAway}) - z = BigFloat() - ccall((:mpfr_round, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) - return z -end - function isinf(x::BigFloat) return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end diff --git a/test/rounding.jl b/test/rounding.jl index a9c183228b5b8..148742c9ad09f 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -272,6 +272,13 @@ end @test round(pi, sigdigits=1) ≈ 3. @test round(pi, sigdigits=3) ≈ 3.14 @test round(pi, sigdigits=4, base=2) ≈ 3.25 + @test round(big(pi)) ≈ big"3." + @test round(big(pi), digits=0) ≈ big"3." + @test round(big(pi), digits=1) ≈ big"3.1" + @test round(big(pi), digits=3, base=2) ≈ big"3.125" + @test round(big(pi), sigdigits=1) ≈ big"3." + @test round(big(pi), sigdigits=3) ≈ big"3.14" + @test round(big(pi), sigdigits=4, base=2) ≈ big"3.25" @test round(10*pi, digits=-1) ≈ 30. @test round(.1, digits=0) == 0. @test round(-.1, digits=0) == -0.