Skip to content

Commit

Permalink
add base keyword to precision/setprecision (JuliaLang#42428)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevengj committed Oct 13, 2021
1 parent 7e81414 commit 54de46e
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 31 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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 == _precision(x)
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
13 changes: 13 additions & 0 deletions test/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1022,3 +1022,16 @@ 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
for (p, b) in ((100,10), (50,100))
setprecision(p, base=b) do
@test precision(BigFloat, base=10) == 100
@test precision(BigFloat, base=100) == 50
@test precision(BigFloat) == precision(BigFloat, base=2) == 333
end
end
end

0 comments on commit 54de46e

Please sign in to comment.