Why do Rationals promote to Floats

Hello,

I’m just getting started with Julia and I ran across something that confused me in this section of the docs; Promotion. It shows the following example:

julia> promote(1, 2.5, 3, 3//4)
(1.0, 2.5, 3.0, 0.75)

I was surprised that the Rational was promoted to a Float. It seems like you can lose accuracy with that conversion. I tried the following and I think this confirms my suspicion:

julia> a,b = promote(0.25, 1//10)
(0.25, 0.1)

julia> @printf("%.55f\n%.55f", a, b)
0.2500000000000000000000000000000000000000000000000000000
0.1000000000000000055511151231257827021181583404541015625

julia> convert(Rational, b)
3602879701896397//36028797018963968

The Float is not exactly 1/10 and the value doesn’t round-trip back to 1/10.

I’m wondering, is there an explanation or discussion about this that I can read? If I wanted to reverse this behavior (convert the Float to a Rational in this case), is that something I can override?

Thanks for any help and insight into this!

2 Likes

I think the reason is that

  1. calculations with eg Rational{Int64} would quickly overflow for the denominator,

  2. the results of transcendental functions (exp) cannot be a rational number in general.

So it is safest to promote to Float64. Of course, the trade-off is floating point inaccuracy.

Generally, I would just not promote but leave it to the operation. For a more specific workaround, please provide more context.

1 Like

Thanks @Tamas_Papp! As I mentioned, I’m just getting started, so my concern may be unfounded. The short version is that I’m porting some code over to Julia to help analyze some math issues I’m seeing. I’d like to be able to run the code using different types of Reals: Float64, BigFloat, and Rational{BigInt}. Most likely, I just need to be careful how I handle any constants in the code outside of zeros and ones. I believe operators are restricted to +, -, *, and /.

To be precise, a Rational and Float64 promote to Float64. (A Rational and an Int promote to a Rational, for example.) The basic principle is that floating-point values are typically inexact approximations for some other real number, so if you are combining them with a rational value you usually expect an inexact result anyway. In that case, converting the rational to the closest floating-point value is not typically a problem.

Keeping things as Rational values during computations, on the other hand, is unusual for the reasons @Tamas_Papp mentioned.

If you want exact rational constants like 1.25 in your code that are automatically converted to the best precision when combined with other values, it is better to represent them as Rational values like 5//4.

7 Likes

Thanks @stevengj!