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

add base keyword to precision/setprecision #42428

Merged
merged 7 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Standard library changes
------------------------

* `range` accepts either `stop` or `length` as a sole keyword argument ([#39241])
* `precision` and `setprecision` now accept a `base` keyword ([#42428]).
* The `length` function on certain ranges of certain specific element types no longer checks for integer
overflow in most cases. The new function `checked_length` is now available, which will try to use checked
arithmetic to error if the result may be wrapping. Or use a package such as SaferIntegers.jl when
Expand Down
26 changes: 20 additions & 6 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -676,17 +676,31 @@ end


"""
precision(num::AbstractFloat)
precision(num::AbstractFloat; base::Integer=2)
precision(T::Type; base::Integer=2)

Get the precision of a floating point number, as defined by the effective number of bits in
the significand.
the significand, or the precision of a floating-point type `T` (its current default, if
`T` is a variable-precision type like [`BigFloat`](@ref)).

If `base` is specified, then it returns the maximum corresponding
number of significand digits in that base.

!!! compat "Julia 1.8"
The `base` keyword requires at least Julia 1.8.
"""
function precision end

precision(::Type{Float16}) = 11
precision(::Type{Float32}) = 24
precision(::Type{Float64}) = 53
precision(::T) where {T<:AbstractFloat} = precision(T)
_precision(::Type{Float16}) = 11
_precision(::Type{Float32}) = 24
_precision(::Type{Float64}) = 53
function _precision(x, base::Integer=2)
base > 1 || throw(DomainError(base, "`base` cannot be less than 2."))
p = _precision(x)
return base == 2 ? Int(p) : floor(Int, p / log2(base))
end
precision(::Type{T}; base::Integer=2) where {T<:AbstractFloat} = _precision(T, base)
precision(::T; base::Integer=2) where {T<:AbstractFloat} = precision(T; base)

"""
uabs(x::Integer)
Expand Down
51 changes: 27 additions & 24 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import
inv, exp, exp2, exponent, factorial, floor, fma, hypot, isinteger,
isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf,
nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float,
sum, sqrt, string, print, trunc, precision, exp10, expm1, log1p,
sum, sqrt, string, print, trunc, precision, _precision, exp10, expm1, log1p,
eps, signbit, sign, sin, cos, sincos, tan, sec, csc, cot, acos, asin, atan,
cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, lerpi,
cbrt, typemax, typemin, unsafe_trunc, floatmin, floatmax, rounding,
Expand Down Expand Up @@ -181,7 +181,7 @@ widen(::Type{Float64}) = BigFloat
widen(::Type{BigFloat}) = BigFloat

function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[])
if precision == MPFR.precision(x)
if precision == MPFR._precision(x)
stevengj marked this conversation as resolved.
Show resolved Hide resolved
return x
else
z = BigFloat(;precision=precision)
Expand All @@ -192,7 +192,7 @@ function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::I
end

function _duplicate(x::BigFloat)
z = BigFloat(;precision=precision(x))
z = BigFloat(;precision=_precision(x))
ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, 0)
return z
end
Expand Down Expand Up @@ -792,37 +792,37 @@ function sign(x::BigFloat)
return c < 0 ? -one(x) : one(x)
end

function precision(x::BigFloat) # precision of an object of type BigFloat
function _precision(x::BigFloat) # precision of an object of type BigFloat
return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ref{BigFloat},), x)
end
precision(x::BigFloat; base::Integer=2) = _precision(x, base)

"""
precision(BigFloat)

Get the precision (in bits) currently used for [`BigFloat`](@ref) arithmetic.
"""
precision(::Type{BigFloat}) = Int(DEFAULT_PRECISION[]) # precision of the type BigFloat itself
_precision(::Type{BigFloat}) = Int(DEFAULT_PRECISION[]) # default precision of the type BigFloat itself

"""
setprecision([T=BigFloat,] precision::Int)
setprecision([T=BigFloat,] precision::Int; base=2)

Set the precision (in bits) to be used for `T` arithmetic.
Set the precision (in bits, by default) to be used for `T` arithmetic.
If `base` is specified, then the precision is the minimum required to give
at least `precision` digits in the given `base`.

!!! warning

This function is not thread-safe. It will affect code running on all threads, but
its behavior is undefined if called concurrently with computations that use the
setting.

!!! compat "Julia 1.8"
The `base` keyword requires at least Julia 1.8.
"""
function setprecision(::Type{BigFloat}, precision::Integer)
if precision < 1
throw(DomainError(precision, "`precision` cannot be less than 1."))
end
DEFAULT_PRECISION[] = precision
function setprecision(::Type{BigFloat}, precision::Integer; base::Integer=2)
base > 1 || throw(DomainError(base, "`base` cannot be less than 2."))
precision > 0 || throw(DomainError(precision, "`precision` cannot be less than 1."))
DEFAULT_PRECISION[] = base == 2 ? precision : ceil(Int, precision * log2(base))
return precision
end

setprecision(precision::Integer) = setprecision(BigFloat, precision)
setprecision(precision::Integer; base::Integer=2) = setprecision(BigFloat, precision; base)

maxintfloat(x::BigFloat) = BigFloat(2)^precision(x)
maxintfloat(::Type{BigFloat}) = BigFloat(2)^precision(BigFloat)
Expand Down Expand Up @@ -916,9 +916,9 @@ floatmin(::Type{BigFloat}) = nextfloat(zero(BigFloat))
floatmax(::Type{BigFloat}) = prevfloat(BigFloat(Inf))

"""
setprecision(f::Function, [T=BigFloat,] precision::Integer)
setprecision(f::Function, [T=BigFloat,] precision::Integer; base=2)

Change the `T` arithmetic precision (in bits) for the duration of `f`.
Change the `T` arithmetic precision (in the given `base`) for the duration of `f`.
It is logically equivalent to:

old = precision(BigFloat)
Expand All @@ -929,19 +929,22 @@ It is logically equivalent to:
Often used as `setprecision(T, precision) do ... end`

Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by
`setprecision`
`setprecision`.

!!! compat "Julia 1.8"
The `base` keyword requires at least Julia 1.8.
"""
function setprecision(f::Function, ::Type{T}, prec::Integer) where T
function setprecision(f::Function, ::Type{T}, prec::Integer; kws...) where T
old_prec = precision(T)
setprecision(T, prec)
setprecision(T, prec; kws...)
try
return f()
finally
setprecision(T, old_prec)
end
end

setprecision(f::Function, prec::Integer) = setprecision(f, BigFloat, prec)
setprecision(f::Function, prec::Integer; base::Integer=2) = setprecision(f, BigFloat, prec; base)

function string_mpfr(x::BigFloat, fmt::String)
pc = Ref{Ptr{UInt8}}()
Expand Down
1 change: 0 additions & 1 deletion doc/src/base/numbers.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ and for [`BigInt`](@ref) the [GNU Multiple Precision Arithmetic Library (GMP)]
```@docs
Base.MPFR.BigFloat(::Any, rounding::RoundingMode)
Base.precision
Base.MPFR.precision(::Type{BigFloat})
Base.MPFR.setprecision
Base.GMP.BigInt(::Any)
Base.@big_str
Expand Down
11 changes: 11 additions & 0 deletions test/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1022,3 +1022,14 @@ end
@test big(typeof(complex(x, x))) == typeof(big(complex(x, x)))
end
end

@testset "precision base" begin
setprecision(53) do
@test precision(Float64, base=10) == precision(BigFloat, base=10) == 15
end
setprecision(100, base=10) do
@test precision(BigFloat, base=10) == 100
@test precision(BigFloat, base=100) == 50
@test precision(BigFloat) == precision(BigFloat, base=2) == 333
end
end