Skip to content

Commit

Permalink
fixes for rationalizing irrationals
Browse files Browse the repository at this point in the history
Fixes several issues:
* Set `BigFloat` precision without mutating the global default.
* Assume just the `:foldable` effect, instead of `:total`.
* Assume effects only for `Irrational`, it's incorrect to do that for
  `AbstractIrrational` in general.
* Avoid infinite loop for `AbstractIrrational`.
* Check for the case of the irrational number getting rounded to an
  integer `BigFloat`.
  • Loading branch information
nsajko committed Sep 24, 2024
1 parent 9f1c068 commit c4735e9
Showing 1 changed file with 44 additions and 12 deletions.
56 changes: 44 additions & 12 deletions base/irrationals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,54 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))

# XXX this may change `DEFAULT_PRECISION`, thus not effect free
@assume_effects :total function Rational{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 _rationalize_irrational_at_current_precision(
::Type{T}, x::AbstractIrrational,
) where {T <: Integer}
bx = BigFloat(x)
if isinteger(bx)
# Either the FP significand or the FP exponent are too narrow.
nothing
else
let r = rationalize(T, bx, tol = 0)
if eps(bx) < abs(1 - r/bx)
r
else
# Error is too small, repeat with greater precision.
nothing
end
end
end
end

function _rationalize_irrational_at_precision(
::Type{T}, x::AbstractIrrational, p::Int,
) where {T <: Integer}
f = let x = x
() -> _rationalize_irrational_at_current_precision(T, x)
end
setprecision(f, BigFloat, p)
end

function _rationalize_irrational(::Type{T}, x::AbstractIrrational) where {T <: Integer}
if T <: BigInt
throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
end
ran = 256:32:65536
for p ran
r = _rationalize_irrational_at_precision(T, x, p)
if r !== nothing
return r
end
p += 32
end
throw(ArgumentError("failed to rationalize irrational"))
end

@assume_effects :foldable function Rational{T}(x::Irrational) where {T <: Integer}
_rationalize_irrational(T, x)
end
function Rational{T}(x::AbstractIrrational) where {T <: Integer}
_rationalize_irrational(T, x)
end
Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))

@assume_effects :total function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64}
setprecision(BigFloat, 256) do
Expand Down

0 comments on commit c4735e9

Please sign in to comment.