-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Changes from 8 commits
c6ba0e9
281c550
29eedee
d5d843b
3ffc946
247e1fd
3346e2a
7ed448b
aec787e
f9337f6
793de08
52e26b3
05a4f9b
d073fd3
2418c85
a89385f
d054dfc
baedebb
03ed3fa
a6dd6a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -324,7 +324,6 @@ export | |
sign, | ||
signbit, | ||
signed, | ||
signif, | ||
significand, | ||
sin, | ||
sinc, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that, but the default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then just omit 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 | ||
|
@@ -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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious: why this change?
There was a problem hiding this comment.
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.