Skip to content

Commit d75ffb5

Browse files
committed
fixes for constructing a Rational from an AbstractIrrational
Fixes several issues: * Set `BigFloat` precision without mutating the global default. * Avoid infinite loop for `AbstractIrrational`. * Check for the case of the irrational number getting rounded to an integer `BigFloat`.
1 parent 9f1c068 commit d75ffb5

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

base/irrationals.jl

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,51 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
5151
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
5252
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
5353

54-
# XXX this may change `DEFAULT_PRECISION`, thus not effect free
55-
@assume_effects :total function Rational{T}(x::AbstractIrrational) where T<:Integer
56-
o = precision(BigFloat)
57-
p = 256
58-
while true
59-
setprecision(BigFloat, p)
60-
bx = BigFloat(x)
61-
r = rationalize(T, bx, tol=0)
62-
if abs(BigFloat(r) - bx) > eps(bx)
63-
setprecision(BigFloat, o)
54+
function _irrational_to_rational_at_current_precision(
55+
::Type{T}, x::AbstractIrrational,
56+
) where {T <: Integer}
57+
bx = BigFloat(x)
58+
if isinteger(bx)
59+
# Either the FP significand or the FP exponent are too narrow.
60+
nothing
61+
else
62+
let r = rationalize(T, bx, tol = 0)
63+
if eps(bx) < abs(1 - r/bx)
64+
r
65+
else
66+
# Error is too small, repeat with greater precision.
67+
nothing
68+
end
69+
end
70+
end
71+
end
72+
73+
function _irrational_to_rational_at_precision(
74+
::Type{T}, x::AbstractIrrational, p::Int,
75+
) where {T <: Integer}
76+
f = let x = x
77+
() -> _irrational_to_rational_at_current_precision(T, x)
78+
end
79+
setprecision(f, BigFloat, p)
80+
end
81+
82+
function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where {T <: Integer}
83+
if T <: BigInt
84+
throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
85+
end
86+
ran = 256:32:65536
87+
for p ran
88+
r = _irrational_to_rational_at_precision(T, x, p)
89+
if r !== nothing
6490
return r
6591
end
66-
p += 32
6792
end
93+
throw(ArgumentError("failed to rationalize irrational"))
94+
end
95+
96+
function Rational{T}(x::AbstractIrrational) where {T <: Integer}
97+
_irrational_to_rational(T, x)
6898
end
69-
Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
7099

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

0 commit comments

Comments
 (0)