Skip to content

Commit

Permalink
Combine signif into round, use keyword args for digits/sigdigits (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Apr 6, 2018
1 parent ff7b645 commit 59eae7d
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 210 deletions.
10 changes: 7 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,9 @@ Library improvements
* `IOBuffer` can take the `sizehint` keyword argument to suggest a capacity of
the buffer ([#25944]).

* `trunc`, `floor`, `ceil`, `round`, and `signif` specify `base` using a
keyword argument. ([#26156])
* `trunc`, `floor`, `ceil`, and `round` specify `digits`, `sigdigits` and `base` using
keyword arguments. ([#26156], [#26670])


Compiler/Runtime improvements
-----------------------------
Expand Down Expand Up @@ -1134,6 +1135,8 @@ Deprecated or removed
* `isupper`, `islower`, `ucfirst` and `lcfirst` have been deprecated in favor of `isuppercase`,
`islowercase`, `uppercasefirst` and `lowercasefirst`, respectively ([#26442]).

* `signif` has been deprecated in favor of the `sigdigits` keyword argument to `round`.

Command-line option changes
---------------------------

Expand Down Expand Up @@ -1442,4 +1445,5 @@ Command-line option changes
[#26286]: https://github.com/JuliaLang/julia/issues/26286
[#26436]: https://github.com/JuliaLang/julia/issues/26436
[#26442]: https://github.com/JuliaLang/julia/issues/26442
[#26600]: https://github.com/JuliaLang/julia/issues/26600
[#26600]: https://github.com/JuliaLang/julia/issues/26600
[#26670]: https://github.com/JuliaLang/julia/issues/26670
15 changes: 6 additions & 9 deletions base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,9 @@ atanh(z::Complex) = atanh(float(z))
#Rounding complex numbers
#Requires two different RoundingModes for the real and imaginary components
"""
round(z, RoundingModeReal, RoundingModeImaginary)
round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]])
round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; digits=, base=10)
round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; sigdigits=, base=10)
Return the nearest integral value of the same type as the complex-valued `z` to `z`,
breaking ties using the specified [`RoundingMode`](@ref)s. The first
Expand All @@ -954,16 +956,11 @@ julia> round(3.14 + 4.5im)
3.0 + 4.0im
```
"""
function round(z::Complex{<:AbstractFloat}, ::RoundingMode{MR}, ::RoundingMode{MI}) where {MR,MI}
Complex(round(real(z), RoundingMode{MR}()),
round(imag(z), RoundingMode{MI}()))
function round(z::Complex, rr::RoundingMode=RoundNearest, ri::RoundingMode=rr; digits=nothing, sigdigits=nothing, base=10)
Complex(round(real(z), rr; digits=digits, sigdigits=sigdigits, base=base),
round(imag(z), ri; digits=digits, sigdigits=sigdigits, base=base))
end
round(z::Complex) = Complex(round(real(z)), round(imag(z)))

function round(z::Complex, digits::Integer; base::Integer = 10)
Complex(round(real(z), digits, base = base),
round(imag(z), digits, base = base))
end

float(z::Complex{<:AbstractFloat}) = z
float(z::Complex) = Complex(float(real(z)), float(imag(z)))
Expand Down
16 changes: 11 additions & 5 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1504,11 +1504,17 @@ end
false)

# PR 26156
@deprecate trunc(x, digits, base) trunc(x, digits, base = base)
@deprecate floor(x, digits, base) floor(x, digits, base = base)
@deprecate ceil(x, digits, base) ceil(x, digits, base = base)
@deprecate round(x, digits, base) round(x, digits, base = base)
@deprecate signif(x, digits, base) signif(x, digits, base = base)
@deprecate trunc(x::Number, digits) trunc(x; digits=digits)
@deprecate floor(x::Number, digits) floor(x; digits=digits)
@deprecate ceil(x::Number, digits) ceil(x; digits=digits)
@deprecate round(x::Number, digits) round(x; digits=digits)
@deprecate signif(x::Number, digits) round(x; sigdigits=digits, base = base)

@deprecate trunc(x::Number, digits, base) trunc(x; digits=digits, base = base)
@deprecate floor(x::Number, digits, base) floor(x; digits=digits, base = base)
@deprecate ceil(x::Number, digits, base) ceil(x; digits=digits, base = base)
@deprecate round(x::Number, digits, base) round(x; digits=digits, base = base)
@deprecate signif(x::Number, digits, base) round(x; sigdigits=digits, base = base)

# issue #25965
@deprecate spawn(cmds::AbstractCmd) run(cmds, wait = false)
Expand Down
1 change: 0 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,6 @@ export
sign,
signbit,
signed,
signif,
significand,
sin,
sinc,
Expand Down
36 changes: 17 additions & 19 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,28 +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,floor(x))
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,ceil(x))
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))
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))

trunc(x::Float64) = trunc_llvm(x)
trunc(x::Float32) = trunc_llvm(x)
trunc(x::Float16) = Float16(trunc(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)

floor(x::Float64) = floor_llvm(x)
floor(x::Float32) = floor_llvm(x)
floor(x::Float16) = Float16(floor(Float32(x)))

ceil(x::Float64) = ceil_llvm(x)
ceil(x::Float32) = ceil_llvm(x)
ceil(x::Float16) = Float16( ceil(Float32(x)))

round(x::Float64) = rint_llvm(x)
round(x::Float32) = rint_llvm(x)
round(x::Float16) = Float16(round(Float32(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))

## floating point promotions ##
promote_rule(::Type{Float32}, ::Type{Float16}) = Float32
Expand Down Expand Up @@ -662,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)))) && (trunc(x) == 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))
Expand All @@ -683,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)))) && (trunc(x) == 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))
Expand Down
194 changes: 101 additions & 93 deletions base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,43 +44,51 @@ isinteger(x::AbstractFloat) = (x - trunc(x) == 0)

"""
round([T,] x, [r::RoundingMode])
round(x, [digits; base = 10])
round(x, [r::RoundingMode]; digits::Integer=0, base = 10)
round(x, [r::RoundingMode]; sigdigits::Integer, base = 10)
Rounds `x` to an integer value according to the provided
[`RoundingMode`](@ref), returning a value of the same type as `x`. When not
specifying a rounding mode the global mode will be used
(see [`rounding`](@ref)), which by default is round to the nearest integer
([`RoundNearest`](@ref) mode), with ties (fractional values of 0.5) being
rounded to the nearest even integer.
Rounds the number `x`.
Without keyword arguments, `x` is rounded to an integer value, returning a value of type
`T`, or of the same type of `x` if no `T` is provided. An [`InexactError`](@ref) will be
thrown if the value is not representable by `T`, similar to [`convert`](@ref).
If the `digits` keyword argument is provided, it rounds to the specified number of digits
after the decimal place (or before if negative), in base `base`.
If the `sigdigits` keyword argument is provided, it rounds to the specified number of
significant digits, in base `base`.
The [`RoundingMode`](@ref) `r` controls the direction of the rounding; the default is
[`RoundNearest`](@ref), which rounds to the nearest integer, with ties (fractional values
of 0.5) being rounded to the nearest even integer. Note that `round` may give incorrect
results if the global rounding mode is changed (see [`rounding`](@ref)).
# Examples
```jldoctest
julia> round(1.7)
2.0
julia> round(Int, 1.7)
2
julia> round(1.5)
2.0
julia> round(2.5)
2.0
```
The optional [`RoundingMode`](@ref) argument will change how the number gets
rounded.
`round(T, x, [r::RoundingMode])` converts the result to type `T`, throwing an
[`InexactError`](@ref) if the value is not representable.
`round(x, digits)` rounds to the specified number of digits after the decimal place (or
before if negative). `round(x, digits, base = base)` rounds using a base other than 10.
# Examples
```jldoctest
julia> round(pi, 2)
julia> round(pi; digits=2)
3.14
julia> round(pi, 3, base = 2)
julia> round(pi; digits=3, base=2)
3.125
julia> round(123.456; sigdigits=2)
120.0
julia> round(357.913; sigdigits=4, base=2)
352.0
```
!!! note
Expand All @@ -104,93 +112,93 @@ julia> round(pi, 3, base = 2)
1.2
```
See also [`signif`](@ref) for rounding to significant digits.
# Extensions
To extend `round` to new numeric types, it is typically sufficient to define `Base._round(x::NewType, ::RoundingMode)`.
"""
round(T::Type, x)
round(x::Real, ::RoundingMode{:ToZero}) = trunc(x)
round(x::Real, ::RoundingMode{:Up}) = ceil(x)
round(x::Real, ::RoundingMode{:Down}) = floor(x)
# C-style round
function round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway})
y = trunc(x)
ifelse(x==y,y,trunc(2*x-y))

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))

function round(x::Real, r::RoundingMode=RoundNearest;
digits::Union{Nothing,Integer}=nothing, sigdigits::Union{Nothing,Integer}=nothing, base=10)
isfinite(x) || return x
_round(x,r,digits,sigdigits,base)
end
# Java-style round
function round(x::AbstractFloat, ::RoundingMode{:NearestTiesUp})
y = floor(x)
ifelse(x==y,y,copysign(floor(2*x-y),x))
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

# round x to multiples of 1/invstep
function _round_invstep(x, invstep, r::RoundingMode)
y = _round(x * invstep, r) / invstep
if !isfinite(y)
return x
end
return y
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
if !isfinite(y)
if x > 0
return (r == RoundUp ? oftype(x, Inf) : zero(x))
elseif x < 0
return (r == RoundDown ? -oftype(x, Inf) : -zero(x))
else
return x
end
end
return y
end
round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T,round(x,r))

# adapted from Matlab File Exchange roundsd: http:https://www.mathworks.com/matlabcentral/fileexchange/26212
# for round, og is the power of 10 relative to the decimal point
# for signif, og is the absolute power of 10
# digits and base must be integers, x must be convertable to float
function _round(x, r::RoundingMode, digits::Integer, sigdigits::Nothing, base)
fx = float(x)
if digits >= 0
invstep = oftype(fx, base)^digits
_round_invstep(fx, invstep, r)
else
step = oftype(fx, base)^-digits
_round_step(fx, step, r)
end
end

function _signif_og(x, digits, base)
hidigit(x::Integer, base) = ndigits0z(x, base)
function hidigit(x::Real, base)
iszero(x) && return 0
fx = float(x)
if base == 10
e = floor(log10(abs(x)) - digits + 1.)
og = oftype(x, exp10(abs(e)))
return 1 + floor(Int, log10(abs(fx)))
elseif base == 2
e = exponent(abs(x)) - digits + 1.
og = oftype(x, exp2(abs(e)))
return 1 + exponent(x)
else
e = floor(log(base, abs(x)) - digits + 1.)
og = oftype(x, float(base) ^ abs(e))
return 1 + floor(Int, log(base, abs(fx)))
end
return og, e
end

"""
signif(x, digits; base = 10)
Rounds (in the sense of [`round`](@ref)) `x` so that there are `digits` significant digits, under a
base `base` representation, default 10.
function _round(x, r::RoundingMode, digits::Nothing, sigdigits::Integer, base)
h = hidigit(x, base)
_round(x, r, sigdigits-h, nothing, base)
end

# Examples
```jldoctest
julia> signif(123.456, 2)
120.0
_round(x, r::RoundingMode, digits::Integer, sigdigits::Integer, base) =
throw(ArgumentError("`round` cannot use both `digits` and `sigdigits` arguments."))

julia> signif(357.913, 4, base = 2)
352.0
```
"""
function signif(x::Real, digits::Integer; base::Integer = 10)
digits < 1 && throw(DomainError(digits, "`digits` cannot be less than 1."))

x = float(x)
(x == 0 || !isfinite(x)) && return x
og, e = _signif_og(x, digits, base)
if e >= 0 # for numeric stability
r = round(x/og)*og
else
r = round(x*og)/og
end
!isfinite(r) ? x : r
# C-style round
function _round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway})
y = trunc(x)
ifelse(x==y,y,trunc(2*x-y))
end

for f in (:round, :ceil, :floor, :trunc)
@eval begin
function ($f)(x::Real, digits::Integer; base::Integer = 10)
x = float(x)
og = convert(eltype(x),base)^digits
r = ($f)(x * og) / og

if !isfinite(r)
if digits > 0
return x
elseif x > 0
return $(:ceil == f ? :(convert(eltype(x), Inf)) : :(zero(x)))
elseif x < 0
return $(:floor == f ? :(-convert(eltype(x), Inf)) : :(-zero(x)))
else
return x
end
end
return r
end
end
# Java-style round
function _round(x::AbstractFloat, ::RoundingMode{:NearestTiesUp})
y = floor(x)
ifelse(x==y,y,copysign(floor(2*x-y),x))
end

# isapprox: approximate equality of numbers
Expand Down
Loading

0 comments on commit 59eae7d

Please sign in to comment.