Skip to content

Commit

Permalink
fixes for constructing a Rational from an AbstractIrrational
Browse files Browse the repository at this point in the history
Fixes several issues:
* Set `BigFloat` precision without mutating the global default, relying
  on the new ScopedValues functionality.
* Avoid reading the global defaults for the precision and rounding
  mode, relies on #56095.
* Try to ensure the loop terminates in a reasonable amount of time, and
  without trying to allocate excessively large `BigFloat` values.
  • Loading branch information
nsajko committed Oct 10, 2024
1 parent a007e80 commit e2cfacb
Showing 1 changed file with 41 additions and 10 deletions.
51 changes: 41 additions & 10 deletions base/irrationals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,50 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))

function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer
o = precision(BigFloat)
p = 256
while true
setprecision(BigFloat, p)
bx = BigFloat(x)
r = rationalize(T, bx, tol=0)
if abs(BigFloat(r) - bx) > eps(bx)
setprecision(BigFloat, o)
function _irrational_to_rational_at_current_precision(
::Type{T}, x::AbstractIrrational,
) where {T <: Integer}
bx = BigFloat(x)
r = rationalize(T, bx, tol = 0)
br_func = let r = r
() -> BigFloat(r)
end
br = setprecision(br_func, BigFloat, precision(BigFloat) + 32)
if eps(bx) < abs(br - bx)
r
else
nothing # Error is too small, repeat with greater precision.
end
end
function _irrational_to_rational_at_precision(
::Type{T}, x::AbstractIrrational, p::Int,
) where {T <: Integer}
f = let x = x
() -> _irrational_to_rational_at_current_precision(T, x)
end
setprecision(f, BigFloat, p)
end
function _irrational_to_rational_at_current_rounding_mode(
::Type{T}, x::AbstractIrrational, precisions,
) where {T <: Integer}
if T <: BigInt
_throw_argument_error_irrational_to_rational_bigint()
end
for p precisions
r = _irrational_to_rational_at_precision(T, x, p)
if r isa Number
return r
end
p += 32
end
throw(ArgumentError("failed to rationalize irrational"))
end
function _irrational_to_rational(
::Type{T}, x::AbstractIrrational, precisions = 256:32:16777216,
) where {T <: Integer}
f = let x = x, precisions = precisions
() -> _irrational_to_rational_at_current_rounding_mode(T, x, precisions)
end
setrounding(f, BigFloat, RoundNearest)
end
Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x)
_throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
Expand Down

0 comments on commit e2cfacb

Please sign in to comment.