Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine signif into round, use keyword args for digits/sigdigits #26670

Merged
merged 20 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1500,11 +1500,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, digits) trunc(x; digits=digits)
@deprecate floor(x, digits) floor(x; digits=digits)
@deprecate ceil(x, digits) ceil(x; digits=digits)
@deprecate round(x, digits) round(x; digits=digits)
@deprecate signif(x, digits) round(x; sigdigits=digits, base = base)

@deprecate trunc(x, digits, base) trunc(x; digits=digits, base = base)
@deprecate floor(x, digits, base) floor(x; digits=digits, base = base)
@deprecate ceil(x, digits, base) ceil(x; digits=digits, base = base)
@deprecate round(x, digits, base) round(x; digits=digits, base = base)
@deprecate signif(x, 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
32 changes: 14 additions & 18 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,28 +348,24 @@ 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)))
_round(x::Float16, r::RoundingMode) = Float16(_round(Float32(x), r))

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

## floating point promotions ##
promote_rule(::Type{Float32}, ::Type{Float16}) = Float32
Expand Down Expand Up @@ -662,7 +658,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)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious: why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

problems with bootstrapping: trunc(x) had to be defined later.

return unsafe_trunc($Ti,x)
else
throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x))
Expand All @@ -683,7 +679,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
184 changes: 90 additions & 94 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= [, base = 10])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

digits::Integer=0, base::Integer=10?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but the default digits = 0 is covered by the above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then just omit =. Though I'm not sure it's a problem to repeat the default. Also brackets are never used for keyword arguments.

BTW, below there's a typo in "provied".

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 provied, 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,81 @@ 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))
end
# Java-style round
function round(x::AbstractFloat, ::RoundingMode{:NearestTiesUp})
y = floor(x)
ifelse(x==y,y,copysign(floor(2*x-y),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))

function round(x, 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
round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T,round(x,r))
trunc(x; kwargs...) = round(x, RoundToZero; kwargs...)
floor(x; kwargs...) = round(x, RoundDown; kwargs...)
ceil(x; kwargs...) = round(x, RoundUp; kwargs...)

_round(x, r::RoundingMode, digits::Nothing, sigdigits::Nothing, base) = _round(x, r)
_round(x::Integer, r::RoundingMode) = x

function _round(x, r::RoundingMode, digits::Integer, sigdigits::Nothing, base)
fx = float(x)
if digits >= 0
sc = oftype(fx, base)^digits
r = round(fx * sc, r) / sc
else
isc = oftype(fx, base)^-digits
r = round(fx / isc, r) * isc
end

# adapted from Matlab File Exchange roundsd: http://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
if !isfinite(r)
if digits > 0
return fx
elseif x > 0
return (r == RoundUp ? oftype(x, Inf) : zero(fx))
elseif x < 0
return (r == RoundDown ? -oftype(x, Inf) : -zero(fx))
else
return fx
end
end
return r
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
23 changes: 12 additions & 11 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -492,49 +492,50 @@ mod(x::Integer, ::Type{T}) where {T<:Integer} = rem(x, T)
unsafe_trunc(::Type{T}, x::Integer) where {T<:Integer} = rem(x, T)

"""
trunc([T,] x, [digits; base = 10])
trunc([T,] x)
trunc(x; digits::Integer= [, base = 10])
trunc(x; sigdigits::Integer= [, base = 10])

`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value
is less than or equal to `x`.

`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is
not representable.

`digits` and `base` work as for [`round`](@ref).
`digits`, `sigdigits` and `base` work as for [`round`](@ref).
"""
function trunc end

"""
floor([T,] x, [digits; base = 10])
floor([T,] x)
floor(x; digits::Integer= [, base = 10])
floor(x; sigdigits::Integer= [, base = 10])

`floor(x)` returns the nearest integral value of the same type as `x` that is less than or
equal to `x`.

`floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is
not representable.

`digits` and `base` work as for [`round`](@ref).
`digits`, `sigdigits` and `base` work as for [`round`](@ref).
"""
function floor end

"""
ceil([T,] x, [digits; base = 10])
ceil([T,] x)
ceil(x; digits::Integer= [, base = 10])
ceil(x; sigdigits::Integer= [, base = 10])

`ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or
equal to `x`.

`ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not
representable.

`digits` and `base` work as for [`round`](@ref).
`digits`, `sigdigits` and `base` work as for [`round`](@ref).
"""
function ceil end

round(x::Integer) = x
trunc(x::Integer) = x
floor(x::Integer) = x
ceil(x::Integer) = x

round(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x)
trunc(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x)
floor(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x)
Expand Down
Loading