Is there an easy way to convert a periodic decimal number into a fraction?
As a mathematician I like not rationalize(0.85555555555)!!
That really leaves a homemade algorithm that handles the periodic part separately? thanks for inspirations.
Albert
This question seems ill-posed to me. See that a floating point number has finite precision and so if you tried converting from a float to a fraction you could not distinguish a periodic decimal part from a finite one.
To do this conversion you can simply do:
julia> convert(Rational, 2.5)
5//2
julia> convert(Rational, 1/3)
6004799503160661//18014398509481984
But as you see converting 1/3=0.3333333333333333 to rational does not give 1//3 and in fact
julia> 1//3 == 1/3
false
Or did you have some other datatype/data structure to convert from in mind?
I agree with @abraemer .
The problem is, there’s no actual way to store an infinitely repeating decimal as a floating point number in any language. You can get somewhat close but it’s just not the same. And it sounds like you rely on that.
For Julia, the number has to be a Rational
from the start. Which you’d construct as shown above.
You ask for an easy way, of which there’s none. But a somewhat more complicated way might be to instead store the repeating digits, which is nicely finite information store as integers. Look at the operations you’re thinking to apply them to and manipulate such operations to account for separate storage of the integers that repeat and the nature of their repetition. Julia’s Irrationals do something similar under the hood, storing the numerator and denominator.
If you want rational results, it’s best to perform the entire calculation with rationals. The Rational
type in Julia propagates through all basic arithmetic. Although functions that (generally) yield irrational results (e.g., sqrt
, sin
, exp
) will convert their result to floats and break such a chain.
julia> x = 2//3
2//3
julia> y = (x * (1 + x) + 2) / 4
7//9
julia> z = sqrt(y*y) # float result, even though there *happens to be* an exact rational result
0.7777777777777778
You can just specify a larger tolerance and Julia will give its “best guess” for a number with a smaller denominator:
julia> rationalize(0.85555555555, tol=1e-8)
77//90
julia> rationalize(0.33333333333, tol=1e-8)
1//3
(\frac{77}{90} is exactly 0.8\bar{5}).
And here is an exact algorithm to convert a.b\bar{c}, specified as strings, to a fraction (ported from the Python solution in this stackexchange discussion):
function decimal2rational(::Type{T}, a::AbstractString, b::AbstractString, c::AbstractString) where {T<:Integer}
czero = isempty(c) || parse(T, c) == 0
numerator = parse(T, a*b*c) - !czero * parse(T, a*b)
denominator = (T(10)^length(c) - !czero) * T(10)^length(b)
return Rational{T}(numerator, denominator)
end
decimal2rational(a::AbstractString, b::AbstractString, c::AbstractString) =
decimal2rational(Int, a, b, c)
For example:
julia> decimal2rational("0", "8", "5") # 0.8555…
77//90
julia> decimal2rational("1", "", "3") # 1.333…
4//3
You can use the package RepeatingDecimalNotations.jl.
julia> using RepeatingDecimalNotations
julia> rd"0.85555..."
77//90
The Julia language and community continues to amaze and suprise me. And I’m so happy to be proven wrong.
Thank you for your contribution. I have learnt two ideas that answer my question:
a) Indicating tolerance with tol, thanks stevengj.
b) Use of the package RepeatingDecimalNotations, thanks greatpet.
I am happy with this!
Albert