# Weird floating point vs rational behavior

**URL:** <https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466>\
**Category:** General Usage\
**Tags:** numerics, rationals, float\
**Created:** [March 22, 2023, 5:37pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466 "2023-03-22T17:37:52Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![DOT](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@DOT](https://discourse.julialang.org/u/DOT)\
**Post date:** [March 22, 2023, 5:37pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/1 "2023-03-22T17:37:53Z")

</div>

Just when you think you’ve seen it all: Could someone please explain this to me?

```julia
julia> 10*1.1 ≤ 11
true

julia> (10//1)*1.1 ≤ (11//1)
true

julia> (10//1)*1.1 - (11//1) ≤ 0
true

julia> 1 ≤ (11//10)/1.1
true

julia> 1.1/(11//10) ≤ 1
true

julia> 1.1 - (11//10) ≤ 0
true

julia> 0 ≤ (11//10) - 1.1
true

julia> 1.1 ≤ 11//10
false 😜

```

It seems that Julia converts floating points to rational via `Rational{Int}(...)` before comparing…?

---

<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:** [March 22, 2023, 5:53pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/2 "2023-03-22T17:53:58Z")

</div>

So the last one is the the only one that is “correct”. Going in order:  
`10*1.1` converts both to floats where `10.0 * 1.1 == 11`. The rest all happen because when doing arithmetic between floats and rationals, both values are `promote`d to floats, while `1.1 ≤ 11//10` actually does the math exactly.

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [March 22, 2023, 6:42pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/3 "2023-03-22T18:42:53Z")

</div>

To elaborate on the above response:

Try `@edit 1.1 <= 11//10` (or `@less` for an in-terminal display).

You’ll see that comparisons between `Rational` and float values are done by calling `Base.decompose`, which represents each number _exactly_ as `num * 2^pow / den`. This is a generalization of both `Rational` (adding the `pow` field) and IEEE floating point values (adding the `den`).

To your point, yes this means that we essentially compute `Rational{Int}(1.1)` (although in practice we still have a `2^pow` multiplier included for large or small numbers). Then the comparison is done (meticulously) versus `11//10` and we find that, in fact, `1.1e0 > 11//10`.

You can also see this in larger (but still finite) precision by inspecting `big(1.1) == 1.100000000000000088817841970012523233890533447265625`.

---

<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:** [March 22, 2023, 7:45pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/4 "2023-03-22T19:45:41Z")

</div>

> [@DOT](#):
>
> It seems that Julia converts floating points to rational

All floating point numbers (aside from `NaN` and `±Inf`) _are_ rational numbers: `Float64` is a particular subset of the rationals. Julia’s float–rational comparisons exploit this to perform an exact comparison.

(The main confusion is that the rational values are not what you think, because they often don’t correspond to a compact decimal representation. As noted above by @mikmoore, `1.1::Float64` is exactly the rational number `1.100000000000000088817841970012523233890533447265625`.)

---

<div class="post-metadata">

**Author:** ![fph](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fph/32/17159_2.png) [@fph](https://discourse.julialang.org/u/fph)\
**Post date:** [March 23, 2023, 7:50am UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/5 "2023-03-23T07:50:26Z")

</div>

Is there a `show` version that displays the exact value of a `Float64`, without truncating it to the first 16 digits, something like the following?

```julia
julia> show(1.1, format=:full)
1.100000000000000088817841970012523233890533447265625

```

I think that making it easily accessible would go a long way in training the users on what to expect from floating point arithmetic. (Having it be the default when a single Float64 is printed in the REPL would be excessive, though.)

---

<div class="post-metadata">

**Author:** ![albheim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albheim/32/34660_2.png) [@albheim](https://discourse.julialang.org/u/albheim)\
**Post date:** [March 23, 2023, 9:08am UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/6 "2023-03-23T09:08:34Z")

</div>

I mean, there is `show(big(1.1))` which will show it and is easily available.

I’m also a bit curious what the printing is actually based on, is it just picking the shortest representation that is closer to the actual value than the neighbouring float values, or what is the rule for printing?

This is where I end up when calling `show(1.0)`, though I’m a bit too lazy to try to figure what is going on there right now…

> <https://github.com/JuliaLang/julia/blob/489d076452130c718c7d77b157b0d503bfc31602/base/ryu/shortest.jl#L227>

The docstring for `Base.Ryu.writeshortest` is

> Convert a float value x into its “shortest” decimal string, which can be parsed back to  
> the same value. This function allows achieving the %g printf format. Note the 2nd method  
> allows passing in a byte buffer and position directly; callers must ensure the buffer has  
> sufficient room to hold the entire decimal string.

which seems to align with my expectation of the shortest representation that is closer to the desired float than any other float.

Trying to call this myself with more precision I only get additional zeros, which I guess could make sense from the docstring description. Though I initially expected it to “be exact” to the provided decimal precision and after that pick the shortest representation.

```julia
julia> Base.Ryu.writeshortest(1.1, false, false, true, 100) # precision = 100
"1.100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

```

---

<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:** [March 23, 2023, 12:28pm UTC](https://discourse.julialang.org/t/weird-floating-point-vs-rational-behavior/96466/7 "2023-03-23T12:28:54Z")

</div>

> [@albheim](#):
>
> I’m also a bit curious what the printing is actually based on, is it just picking the shortest representation that is closer to the actual value than the neighbouring float values, or what is the rule for printing?

For `Float64/32/16`, it’s printing the _shortest_ decimal value that _rounds to the same value_ (when rounded to the nearest binary floating point number).

In particular, it uses a state-of-the-art algorithm called [Ryū](https://dl.acm.org/doi/10.1145/3192366.3192369). (Previously it used another algorithm called Grisu that did the same thing.) In particular, as described in the Ryū paper, the criteria for display are:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/e/a/ea9a5d2368594136862f8180b796202a186899a8.png)

as laid out in a [classic 1990 paper](https://dl.acm.org/doi/10.1145/989393.989431).

See also these posts:

- [Working with machine precision - #9 by kristoffer.carlsson](https://discourse.julialang.org/t/working-with-machine-precision/29576/9)
- [Are BigFloats unable to exactly represent 0.1? - #6 by Sukera](https://discourse.julialang.org/t/are-bigfloats-unable-to-exactly-represent-0-1/62192/6)
- [Sum of float64 vector gives slightly incorrect answer - #20 by stevengj](https://discourse.julialang.org/t/sum-of-float64-vector-gives-slightly-incorrect-answer/8577/20)

and many others… (Note, however, that `BigFloat` printing uses a separate algorithm implemented in the GNU MPFR library, and IIRC it is _not_ always the shortest decimal representation.)
