Decimal2rational

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?

1 Like

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.

1 Like

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
1 Like

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
3 Likes

You can use the package RepeatingDecimalNotations.jl.

julia> using RepeatingDecimalNotations

julia> rd"0.85555..."
77//90
4 Likes

The Julia language and community continues to amaze and suprise me. And I’m so happy to be proven wrong.

1 Like

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

1 Like