Skip to content

Commit

Permalink
handle round(x; base=...) (JuliaLang#28199)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Jul 23, 2018
1 parent e6f5098 commit ce9bc6c
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 11 deletions.
35 changes: 24 additions & 11 deletions base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions test/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ce9bc6c

Please sign in to comment.