Should InexactError be thrown when converting from Int64 to Float64?

Converting from Float64 to Int64 works fine if the number is an integer, otherwise a InexactError is thrown, and the user is required to use a function such as round or floor to perform the conversion in general.

julia> Int64(1.0)
1

julia> Int64(1.1)
ERROR: InexactError: Int64(1.1)
Stacktrace:
 [1] Int64(x::Float64)
   @ Base ./float.jl:994
 [2] top-level scope
   @ REPL[34]:1

The converse is not always exact, though:

julia> 2^53 == Float64(2^53)
true

julia> 2^53+1 == Float64(2^53+1)
false

There are Int64 numbers that cannot be represented with enough accuracy in Float64. In a way, it’s the same problem: one type can represent values with more accuracy than the other, accuracy is being lost in the conversion. Shouldn’t the same InexactError be throw in this case?

This has practical importance: if you represent dates or geographical coordinates you often have numbers varying around a large bias, and it may work fine as fixed-point integers, but converting to Float will lose precision.

3 Likes

No because

  • it’s breaking, we’re long past that
  • we don’t have to throw an error when its documented condition is met. It could be handled another way, or we could opt out in exception handling.

Converting to a type with lower precision is accepted as silent or warned approximation in many languages. Conversions to lower precision floating point logically could’ve gone the way you’re proposing, but it’s already unusual to even throw InexactErrors in this many situations.

1 Like

It’s also worth noting that the Float* constructors all round in many other situations, too. For example:

julia> Float64(2//3)
0.6666666666666666

julia> Float32(1.00000001)
1.0f0

In fact, the documentation says this is a rounding operation — and you can pass an optional RoundingMode argument to specify how it should happen.

julia> Float64(2//3, RoundUp)
0.6666666666666667

julia> Float32(1.00000001, RoundUp)
1.0000001f0
3 Likes

This was discussed in should convert(Float64, 2^53+1) be an InexactError? · Issue #12187 · JuliaLang/julia · GitHub back in 2015.

The basic argument here is that construction of floating-point values, whether from integers, or rationals like 1//3, arithmetic expressions like 1/3, or literal values like 0.1, is commonly understood to involve rounding. Requiring the rounding to be explicit just in the constructor/convert context would therefore be unnecessarily awkward.

That’s not the case with types like Int or Rational, where operations are understood to be exact unless rounding is requested explicitly.

6 Likes

Thanks everyone for the insightful comments, I couldn’t find the previous discussion. I guess in the end rounding is part of life when your result is a float, and the fact you can exactly convert some floats to int is actually kind of a quirk.