From c15a97997ea6fce892c0bdd12b436b9eecfbea9f Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 19 Jul 2018 14:28:36 -0700 Subject: [PATCH] handle round(x; base=...) Fixes #28160. --- base/floatfuncs.jl | 35 ++++++++++++++++++++++++----------- test/rounding.jl | 1 + 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 82e2ef7119c8b..68dad13bcdfa8 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -123,19 +123,35 @@ round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T # 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) + digits::Union{Nothing,Integer}=nothing, sigdigits::Union{Nothing,Integer}=nothing, base::Union{Nothing,Integer}=nothing) isfinite(x) || return x - _round(x,r,digits,sigdigits,base) + if digits === nothing + if sigdigits === nothing + if base === nothing + # avoid recursive calls + throw(MethodError(round, (x,r))) + else + round(x,r) + # or throw(ArgumentError("`round` cannot use `base` argument without `digits` or `sigdigits` arguments.")) + end + else + _round_sigdigits(x, r, sigdigits, base === nothing ? 10 : base) + end + else + if sigdigits === nothing + _round_digits(x, r, digits, base === nothing ? 10 : base) + else + throw(ArgumentError("`round` cannot use both `digits` and `sigdigits` arguments.")) + end + end end + 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::Integer, r::RoundingMode) = x -# if we hit this method, it means that no `round(x, r)` method is defined -_round(x::Real, r::RoundingMode, digits::Nothing, sigdigits::Nothing, base) = throw(MethodError(round, (x,r))) - # round x to multiples of 1/invstep function _round_invstep(x, invstep, r::RoundingMode) y = round(x * invstep, r) / invstep @@ -161,7 +177,7 @@ function _round_step(x, step, r::RoundingMode) return y end -function _round(x, r::RoundingMode, digits::Integer, sigdigits::Nothing, base) +function _round_digits(x, r::RoundingMode, digits::Integer, base) fx = float(x) if digits >= 0 invstep = oftype(fx, base)^digits @@ -185,14 +201,11 @@ function hidigit(x::AbstractFloat, base) end hidigit(x::Real, base) = hidigit(float(x), base) -function _round(x, r::RoundingMode, digits::Nothing, sigdigits::Integer, base) +function _round_sigdigits(x, r::RoundingMode, sigdigits::Integer, base) h = hidigit(x, base) - _round(x, r, sigdigits-h, nothing, base) + _round_digits(x, r, sigdigits-h, base) end -_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}) y = trunc(x) diff --git a/test/rounding.jl b/test/rounding.jl index a5cebccea5941..f9537180f0616 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -211,6 +211,7 @@ end # custom rounding and significant-digit ops @testset "rounding to digits relative to the decimal point" begin @test round(pi) ≈ 3. + @test round(pi, base=10) ≈ 3. @test round(pi, digits=0) ≈ 3. @test round(pi, digits=1) ≈ 3.1 @test round(pi, digits=3, base=2) ≈ 3.125