# Float construction function is lossy

**URL:** <https://discourse.julialang.org/t/float-construction-function-is-lossy/104415>\
**Category:** Internals & Design\
**Created:** [September 29, 2023, 10:55pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415 "2023-09-29T22:55:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [September 29, 2023, 10:55pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/1 "2023-09-29T22:55:27Z")

</div>

```julia
julia> let x = 2^53 + 1
           Int(float(x)) - x
       end
-1

```

How do we feel about `float(x)` being lossy? That behavior is not documented. I’m wondering if it should throw an error.

---

<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:** [September 29, 2023, 11:05pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/2 "2023-09-29T23:05:55Z")

</div>

There really isn’t a good alternative. Do you want `1/3` to error? If so, how do you want people to construct the closest Float64 to `1//3`? Should people be required to write `6004799503160661/18014398509481984`

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 30, 2023, 12:33am UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/3 "2023-09-30T00:33:54Z")

</div>

IIRC Float64’s unsigned significand implies 1 bit for the leading 1 and stores the next 52 bits, ranging 2^52:2^53-1. Smaller positive integers and larger integers with enough trailing 0s can be perfectly represented by this significand range along with the exponent.

So squinting at this example, you provided an integer with too many bits between the leading and trailing 1s for `Float64` to store losslessly. -2^53:2^53 is all safe, e.g. 2^52+1 to make all stored bits 0s except the trailing bit.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [September 30, 2023, 8:37pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/4 "2023-09-30T20:37:28Z")

</div>

What about with `convert`? I’m less comfortable with losing information implicitly in `convert` than in an explicit call to `float`.

```julia
julia> x = 2^53+1
9007199254740993

julia> Int(convert(Float64, x))
9007199254740992

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [September 30, 2023, 9:14pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/5 "2023-09-30T21:14:19Z")

</div>

> [@jar1](#):
>
> What about with `convert`?

That is also explicitly documented to be lossy for `AbstractFloat` types:

```julia
help?> convert
search: convert const collect

  convert(T, x)

  Convert x to a value of type T.

[...]

  If `T` is a `AbstractFloat` type, then it will return the closest value to `x` representable by `T`.

[...]

```

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [September 30, 2023, 9:20pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/6 "2023-09-30T21:20:16Z")

</div>

Documented yes but good?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 30, 2023, 11:14pm UTC](https://discourse.julialang.org/t/float-construction-function-is-lossy/104415/7 "2023-09-30T23:14:21Z")

</div>

> [@jar1](#):
>
> What about with `convert`? I’m less comfortable with losing information implicitly in `convert` than in an explicit call to `float`.

`convert` and `float` both end up doing the same `Float64(x)` call anyway, and all methods with implicit promotion do a `T(x)` call at some point. The `T(x)` call is where an `InexactError` would happen for integers.

Floating points are designed to continue through precision loss. It doesn’t make much sense (and is too late for both Julia v1 and IEEE-754) to throw errors in one specific case. I don’t think anybody is really “comfortable,” we’d all prefer if we never lost precision, but that’s not possible in memory with fixed size.

If you really must check that the lossy methods didn’t lose precision, you could try afterwards. It’s easy in this case:

```julia
julia> function exactfloat(x::Integer)
         xf = float(x)
         x == xf ? xf : throw(InexactError(:exactfloat, typeof(xf), x))
       end
exactfloat (generic function with 1 method)

julia> exactfloat(2^53+1)
ERROR: InexactError: exactfloat(Float64, 9007199254740993)
...

```

Admittedly, that error message isn’t quite right, but point is you can do anything after the check, including throwing an error.
