# Convert Rational types to float

**URL:** <https://discourse.julialang.org/t/convert-rational-types-to-float/135767>\
**Category:** General Usage\
**Created:** [February 21, 2026, 7:41pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767 "2026-02-21T19:41:12Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 21, 2026, 7:41pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/1 "2026-02-21T19:41:12Z")

</div>

Hello everyone,

when converting a Rational type to floating point I’m expecting the result to be up to machine precision. In other words `Float64(a//b)` should be _the closest floating point number to a/b_.

I found some instances that do not satisfy this requirement, for example

```julia-auto
julia> r = 18940981//3
18940981//3

julia> Float32(r) == Float32(Float64(r))
false

```

here I’m using `Float64` as a proxy for an higher precision calculation. This happens because the numerator is not exactly representable as `Float32`. I think there exist examples for `Float64`, but I don’t have one at the moment (I mined this example)

First of all, is my logic correct? Should we care about improving the accuracy of such conversions? I’m using rational numbers to compute high order finite difference stencils, I’m converting them back to floating point when assembling a matrix, so it makes sense to have the most accurate representation

Here is the julia implementation of the conversion:

```julia-auto
AbstractFloat(x::Rational) = (float(x.num)/float(x.den))::AbstractFloat
function (::Type{T})(x::Rational{S}) where T<:AbstractFloat where S
    P = promote_type(T,S)
    convert(T, convert(P,x.num)/convert(P,x.den))::T
end

```

I have also checked the boost rational implementation and they use the same logic. First convert to the floating point type and then perform the division

---

<div class="post-metadata">

**Author:** ![technocrat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/technocrat/32/220947_2.png) [@technocrat](https://discourse.julialang.org/u/technocrat)\
**Post date:** [February 21, 2026, 8:18pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/2 "2026-02-21T20:18:37Z")

</div>

```julia-auto
julia> num, den = 18940981, 3
julia> Float32(num) / Float32(den) # Direct path
6313660.333f0
julia> Float32(Float64(num) / Float64(den)) # Two-step path
6313660.3335f0 # Note subtle difference

```

The inequality arises because converting the Rational{Int64} 18940981//3 (exactly 6313660 + 1/3) to Float32 directly differs from converting it first to Float64 (higher precision) and then to Float32. [On conversion and promotion](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 21, 2026, 8:43pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/3 "2026-02-21T20:43:17Z")

</div>

> [@FattiMei](#):
>
> This happens because the numerator is not exactly representable as `Float32`. I think there exist examples for `Float64`, but I don’t have one at the moment (I mined this example)

Right, the algorithm of first converting the numerator and denominator to the floating-point type, and _then_ doing the division, rounds twice whenever the numerator or denominator is not exactly representable, so it is fundamentally not guaranteed to give you exact rounding.

An example for `Float64` is:

```julia-auto
julia> r2 = (Int(maxintfloat(Float64))+1234567)//7
9007199255975559//7

julia> setprecision(BigFloat, 256);

julia> Float64(r2) == Float64(BigFloat(r2))
false

```

and one can verify that `Float64(r2)` is indeed not the closest `Float64` value:

```julia-auto
julia> abs(Float64(r2) - BigFloat(r2)) > abs(Float64(BigFloat(r2)) - BigFloat(r2))
true

```

However, I’m not aware of an algorithm that will do better without going to higher precision (or some kind of guard bits).

It’s not crazy for us to improve the conversion of `Rational` to `Float32` (or `Float16`) by converting to `Float64` first.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [February 22, 2026, 3:48am UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/4 "2026-02-22T03:48:53Z")

</div>

> [@stevengj](#):
>
> I’m not aware of an algorithm that will do better without going to higher precision

How does parsing of float literals work? All literals are rational. Granted, literals can only express rationals whose denominators are powers of 10, but powers of 10 aren’t particularly nice in binary, so I’m curious how hard it would be to generalize the algorithm.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 22, 2026, 12:25pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/5 "2026-02-22T12:25:38Z")

</div>

> [@danielwe](#):
>
> How does parsing of float literals work? All literals are rational. Granted, literals can only express rationals whose denominators are powers of 10, but powers of 10 aren’t particularly nice in binary, so I’m curious how hard it would be to generalize the algorithm.

Float parsing is simpler because the limited set of denominators allows the use of precomputed tables, but my understanding is that it may still use use higher precision at intermediate steps.

Suppose that the literal has fewer than 19 digits, so that the exact numerator is representable as a `UInt64`, but it is bigger than 2^{53} so it is not representable as a `Float64`. In that case, for example, [Lemire (2021)](https://onlinelibrary.wiley.com/doi/full/10.1002/spe.2984?casa_token=CzwkR1cr1ckAAAAA%3AMwEQBBi3110LcVXLU6rEXBfq0F3qhv6uKtIIHfKsxEUsE8vXMpcHv1Aaqf-9KAxLMRf_nRr_f4ibqeo) “relies on a precomputed table of **128-bit** values _T_[_q_] for decimal exponents _q_ ∈ [−342, 308]”, and even then it must occasionally “fall back on a higher-precision approach in uncommon instances” for _q_ ∉ [−27, 55].

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [February 22, 2026, 2:01pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/6 "2026-02-22T14:01:02Z")

</div>

Note that the performance of parsing a literal is nowhere near as performance sensitive as runtime computations that need to happen in a tight loop.

If course it’s always good to have parsing be faster, but taking e.g. 20ns extra to do some higher precision calculations is a drop in the bucket.

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 22, 2026, 4:42pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/7 "2026-02-22T16:42:45Z")

</div>

Can we first separate the rational number into integer and fractional part? This way

1. if the integer part is already non-exactly representable the fractional part won’t affect the result
2. else the rounding affects only the fractional part

Regarding comments about the performance, an accurate conversion is made at the end of long rational calculations. If we “cash out” it makes sense to have the highest precision possible

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [February 22, 2026, 5:22pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/8 "2026-02-22T17:22:09Z")

</div>

you’re misdiagnosing the problem here. the float32 version is correct. the computation that uses extra precision is incorrect do to double rounding.

There are cases where our rational-\>float code does the wrong thing, but this isn’t one of them

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 22, 2026, 5:58pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/9 "2026-02-22T17:58:56Z")

</div>

It’s the opposite. The first rounding is exact in Float64 and inexact in Float32. So the Float64 version performs only one rounding, while the Float32 performs 2

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 22, 2026, 6:47pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/10 "2026-02-22T18:47:02Z")

</div>

> [@FattiMei](#):
>
> It’s the opposite. The first rounding is exact in Float64 and inexact in Float32. So the Float64 version performs only one rounding, while the Float32 performs 2

Right, and in particular you can see that the `Float32(num // den)` version is _farther_ from the exact rational than `Float32(Float64(num // den))`, so the former is _not_ the “correctly rounded” (closest) `Float32` value:

```julia-auto
julia> r = 18940981//3
18940981//3

julia> f1, f2 = Float32(r), Float32(Float64(r))
(6.31366f6, 6.3136605f6)

julia> rationalize(f1; tol=0) == f1
true

julia> rationalize(f2; tol=0) == f2
true

julia> rationalize(f1; tol=0) - r, rationalize(f2; tol=0) - r
(-1//3, 1//6)

```

In particular, note that `Float32(r)` differs from `r` by exactly -\frac{1}{3}, while `Float32(Float64(r))` differs from `r` by exactly \frac{1}{6}, and \frac{1}{3} \> \frac{1}{6} so the former is not correctly rounded.

```julia-auto

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 22, 2026, 6:52pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/11 "2026-02-22T18:52:48Z")

</div>

> [@FattiMei](#):
>
> Can we first separate the rational number into integer and fractional part?

I’m not sure why that would ensure correct rounding? You still have multiple rounding steps in general.

> [@FattiMei](#):
>
> Regarding comments about the performance, an accurate conversion is made at the end of long rational calculations. If we “cash out” it makes sense to have the highest precision possible

I agree that performance may not be crucial for this operation. Certainly it seems worthwhile to use `Float64` even if the final result is `Float32`. Not sure if it is worth it to use `BigFloat`?

(Personally, I often use rational to express literal constants in precision-independent code, in which case the conversion is done at _compile_-time thanks to constant-propagation: [julia#32024](https://github.com/JuliaLang/julia/issues/32024).)

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [February 22, 2026, 6:57pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/12 "2026-02-22T18:57:32Z")

</div>

The advantage of splitting is that you can use an integer `div` to compute the integral part of the answer exactly, reducing the size of the problem a good deal.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 22, 2026, 7:12pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/13 "2026-02-22T19:12:59Z")

</div>

> [@Oscar\_Smith](#):
>
> The advantage of splitting is that you can use an integer `div` to compute the integral part of the answer exactly, reducing the size of the problem a good deal.

I don’t see how it reduces the problem much in general, since if the denominator is too big to be represented then you will still incur multiple rounding steps in dealing with the fractional part.

And, of course, it doesn’t help at all with rational numbers where the integral part is zero. For example, with:

```julia-auto
r = 603869355627654//2358152114619205 # ≈ 0.426…

```

you have the same problem: `Float32(r)` is not correctly rounded to the closest `Float32` value.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 22, 2026, 7:19pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/14 "2026-02-22T19:19:52Z")

</div>

> [@FattiMei](#):
>
> Regarding comments about the performance, an accurate conversion is made at the end of long rational calculations. If we “cash out” it makes sense to have the highest precision possible

Another thing to keep in mind is that if your rational numbers have huge denominators and you are doing a big rational calculation, they are very likely to overflow `Int`. And if you are using `BigInt` you _really_ don’t care about performance for the final `Float32` or `Float64` conversion.

Whereas for small rational constants like `1//3` there is no problem, and we can quickly check for this fast path by comparing to `maxintfloat`.

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 23, 2026, 11:03am UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/15 "2026-02-23T11:03:58Z")

</div>

Is there any chance of updating the documentation or writing a comment near the source code? At least we will warn users of possible loss of precision

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 23, 2026, 1:50pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/16 "2026-02-23T13:50:17Z")

</div>

I’d rather fix it to be correctly rounded, e.g.

```julia-auto
Float16(r::Rational) = Float16(Float64(r))
Float32(r::Rational) = Float32(Float64(r))
function Float64(r::Rational)
    fnum, fden = Float64(r.num), Float64(r.den)
    if fnum == r.num && fden == r.den # fast path
        return fnum / fden
    else
        # slow path: use BigFloat to ensure correct rounding
    end
end

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [February 23, 2026, 1:57pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/17 "2026-02-23T13:57:07Z")

</div>

I wonder if we could take advantage of `Base.TwicePrecision` to do the slow path more efficiently than the very slow `BigFloat` path.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 23, 2026, 2:14pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/18 "2026-02-23T14:14:50Z")

</div>

> [@Mason](#):
>
> I wonder if we could take advantage of `Base.TwicePrecision`

Right, I forgot about this type. For `Rational{Int64}` this should work, I think? We’d need to implement a better constructor from `Int64` (this [seems like a bug](https://github.com/JuliaLang/julia/blob/74b7adc233b6a23a8f38eedd3830ee99cc6ac161/base/twiceprecision.jl#L18-L20), in fact):

```julia-auto
julia> Base.TwicePrecision{Float64}(typemax(Int))
ERROR: InexactError: Int64(9.223372036854776e18)

```

For `Rational{BigInt}` you’ll end up needing `BigFloat`, but it hardly matters there.

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 23, 2026, 5:09pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/19 "2026-02-23T17:09:08Z")

</div>

In the case we want to limit the use of bigger precision computation I was able to figure out one of the branches in the decision tree. Pardon the pseudocode

```julia-auto
if denominator is representable
  if numerator is representable
    trivial case
  else
    produce integral and fractional part
    now the fractional part is composed of representable quantities
    divide them using the target precision
    sum to the integral part

```

When the denominator is not representable things gets a little more complicated… I still have to figure it out

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [February 24, 2026, 5:33pm UTC](https://discourse.julialang.org/t/convert-rational-types-to-float/135767/20 "2026-02-24T17:33:09Z")

</div>

I have an update on the proposed conversion routine:  
even the happy case where the denominator is representable but the numerator may give an error greater than the representation one due to double rounding…

The general solution would be to manually compute the mantissa bits via shifts and integer division. I’m currently doing a small performance study in which I document the problem and design decisions, at the end it would be a speed comparison between a manual routine and the conversion to `BigFloat`.

The problem is quite interesting and there are only a [few online resources](https://cs.stackexchange.com/questions/159999/convert-a-rational-number-to-a-floating-point-number-exactly), once the analysis is completed I would like to do a PR with the implementation and some docs
