# Surprise with Rational

**URL:** <https://discourse.julialang.org/t/surprise-with-rational/88694>\
**Category:** New to Julia\
**Tags:** rationals\
**Created:** [October 13, 2022, 9:51pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694 "2022-10-13T21:51:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![thattommyhall](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thattommyhall/32/19205_2.png) [@thattommyhall](https://discourse.julialang.org/u/thattommyhall)\
**Post date:** [October 13, 2022, 9:51pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/1 "2022-10-13T21:51:20Z")

</div>

I dont understand why 0 \* 1//0 blows up

```julia
julia> 1//0 * 2
1//0

```

```julia
julia> 1//0 * 1
1//0

```

```julia
julia> 1//0 * 0
ERROR: DivideError: integer division error
Stacktrace:
 [1] div
   @ ./int.jl:288 [inlined]
 [2] divgcd(x::Int64, y::Int64)
   @ Base ./rational.jl:45
 [3] *(x::Rational{Int64}, y::Int64)
   @ Base ./rational.jl:339
 [4] top-level scope
   @ REPL[63]:1

```

```julia
julia> 1//0 * zero(Rational)
ERROR: DivideError: integer division error
Stacktrace:
 [1] div
   @ ./int.jl:288 [inlined]
 [2] divgcd(x::Int64, y::Int64)
   @ Base ./rational.jl:45
 [3] *(x::Rational{Int64}, y::Rational{Int64})
   @ Base ./rational.jl:335
 [4] top-level scope
   @ REPL[64]:1

```

```julia
julia> 1//0 * 0//3
ERROR: DivideError: integer division error
Stacktrace:
 [1] div
   @ ./int.jl:288 [inlined]
 [2] divgcd(x::Int64, y::Int64)
   @ Base ./rational.jl:45
 [3] *(x::Rational{Int64}, y::Rational{Int64})
   @ Base ./rational.jl:335
 [4] top-level scope
   @ REPL[65]:1

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 13, 2022, 9:55pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/2 "2022-10-13T21:55:32Z")

</div>

What part do you find surprsing?

0/0 is not well defined. It’s analogous to what IEEE has:

```julia
julia> 0.0 / 0.0
NaN

julia> 1.0 / 0.0
Inf

```

except here we don’t have NaN because you’re not dealing with IEEE floating point numbers, so blow up is pretty reasonable

---

<div class="post-metadata">

**Author:** ![thattommyhall](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thattommyhall/32/19205_2.png) [@thattommyhall](https://discourse.julialang.org/u/thattommyhall)\
**Post date:** [October 13, 2022, 9:56pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/3 "2022-10-13T21:56:21Z")

</div>

My example is using `*`

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 13, 2022, 9:56pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/4 "2022-10-13T21:56:54Z")

</div>

so?

for rational numbers, (a//b) \* c is exactly the same as (a\*c) // (b), in the end you have `0//0` no?

I mean observe:

```julia
julia> float(1//0)
Inf

julia> float(2//0)
Inf

```

---

<div class="post-metadata">

**Author:** ![thattommyhall](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thattommyhall/32/19205_2.png) [@thattommyhall](https://discourse.julialang.org/u/thattommyhall)\
**Post date:** [October 13, 2022, 10:03pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/5 "2022-10-13T22:03:43Z")

</div>

`(a//b) * c` being same as `(a*c) // b` and `0//0` being an error is a useful way to think about it, thanks

(its confusing that its a different error though)

```julia
julia> 0//0
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)
Stacktrace:
 [1] __throw_rational_argerror_zero(T::Type)
   @ Base ./rational.jl:32
 [2] Rational
   @ ./rational.jl:34 [inlined]
 [3] Rational
   @ ./rational.jl:39 [inlined]
 [4] //(n::Int64, d::Int64)
   @ Base ./rational.jl:62
 [5] top-level scope
   @ REPL[67]:1

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 13, 2022, 10:05pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/6 "2022-10-13T22:05:16Z")

</div>

that’s thrown by constructor, you’re doing arithmetic, where an intermediate step throws error already, I mean, I don’t think they are confusing since they are pretty much saying the same thing: you can’t `\div` by integer 0

```julia
julia> 1÷0
ERROR: DivideError: integer division error
Stacktrace:
 [1] div(x::Int64, y::Int64)
   @ Base ./int.jl:288
 [2] top-level scope
   @ REPL[2]:1

```

but yeah, maybe we can improve this somehow

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [October 13, 2022, 10:43pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/7 "2022-10-13T22:43:31Z")

</div>

When is it rational (pun intended) to define 1//0 (or even better 2//0)? Should it be disallowed in the constructor (it would be slightly faster, but breaking, not in a major way?). It’s similar to defining Inf (or -Inf), and I’m not sure how often you do that on purpose (for floats, or at least rationals). Julia could have a different way of defining Inf\_rat, and also a show methods that shows Inf\_rat, not 1//0 or 2//0 etc.

The error message seems by design, but could be amended, and Julia as fast:

```julia
@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)"))
function Rational{T}(num::Integer, den::Integer) where T<:Integer
    iszero(den) && iszero(num) && __throw_rational_argerror_zero(T)
    num, den = divgcd(num, den)
    return checked_den(T, T(num), T(den))
end

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 13, 2022, 10:55pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/8 "2022-10-13T22:55:18Z")

</div>

Whatever it is, it’s breaking change

---

<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:** [October 14, 2022, 12:25am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/9 "2022-10-14T00:25:13Z")

</div>

> [@Palli](#):
>
> When is it rational (pun intended) to define 1//0 (or even better 2//0)? Should it be disallowed in the constructor

It was intentionally allowed in order to represent `Inf` with a `Rational`:

> <https://github.com/JuliaLang/julia/issues/20349>
>
> \`\`\`julia
> julia\> 1//0
> 1//0
> 
> julia\> div(1,0)
> ERROR: DivideError: integer divi…sion error
> Stacktrace:
> \[1\] div(::Int64, ::Int64) at ./int.jl:144
> \`\`\`
> 
> Should \`1//0\` also error out?

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 14, 2022, 4:35am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/10 "2022-10-14T04:35:07Z")

</div>

> [@stevengj](#):
>
> It was intentionally allowed in order to represent `Inf` with a `Rational`:

By the same spirit, why wouldn’t `0//0` be allowed in order to represent `NaN` with a `Rational`?

The inconsistency is noteworthy and causes some hilariously bad behavior. Take some code with `Float64`:

```julia
julia> harmonicmean(a, b) = 2/(1/a + 1/b);

julia> harmonicmean(1, 1/0) # note 1/0 == Inf
2.0

julia> harmonicmean(1, (1+1im)/0) # note (1+1im)/0 == Inf + Inf*im
2.0 + 0.0im

julia> harmonicmean(1, (1+0im)/0) # note (1+0im)/0 == Inf + NaN *im
2.0 - 0.0im

julia> harmonicmean(1, 1im/0) # note 1im/0 == NaN + Inf*im
2.0 + 0.0im

```

All this behavior is expected; just dealing with different versions of infinity strewn about the complex plane.

Compare with `Rational`:

```julia
julia> harmonicmean(a::Rational, b::Rational) = 2//(1//a + 1//b);

julia> harmonicmean(1//1, 1//0)
2//1

julia> harmonicmean(1//1, (1+1im)//0)
2//1 + 0//1*im

julia> harmonicmean(1//1, (1+0im)//0)
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)
Stacktrace:
 [1] __throw_rational_argerror_zero(T::Type)
   @ Base .\rational.jl:32
 [2] Rational
   @ .\rational.jl:34 [inlined]
 [3] Rational
   @ .\rational.jl:39 [inlined]
 [4] //
   @ .\rational.jl:62 [inlined]
 [5] //(x::Complex{Int64}, y::Int64)
   @ Base .\rational.jl:78
 [6] top-level scope
   @ REPL[42]:1

julia> harmonicmean(1//1, 1im//0)
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)
Stacktrace:
 [1] __throw_rational_argerror_zero(T::Type)
   @ Base .\rational.jl:32
 [2] Rational
   @ .\rational.jl:34 [inlined]
 [3] Rational
   @ .\rational.jl:39 [inlined]
 [4] //
   @ .\rational.jl:62 [inlined]
 [5] //(x::Complex{Int64}, y::Int64)
   @ Base .\rational.jl:78
 [6] top-level scope
   @ REPL[69]:1

```

Sure the error is thrown before the call to `harmonicmean`, but that’s not the point.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 14, 2022, 4:44am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/11 "2022-10-14T04:44:07Z")

</div>

Hm… 🤔

```julia
julia> (1/0 + 0im)
Inf + 0.0im

julia> (1//0 + 0im)
1//0 + 0//1*im

julia> (1/0 + 0im)^-1
0.0 - 0.0im

julia> (1//0 + 0im)^-1
0//1 + 0//1*im

julia> 1/(1/0 + 0im)
0.0 - 0.0im

julia> 1/(1//0 + 0im)
0//1 + 0//1*im

julia> 1//(1//0 + 0im)
ERROR: DivideError: integer division error
Stacktrace:
 [1] div
   @ .\int.jl:288 [inlined]
 [2] divgcd(x::Int64, y::Int64)
   @ Base .\rational.jl:45
 [3] //
   @ .\rational.jl:74 [inlined]
 [4] //(x::Complex{Rational{Int64}}, y::Rational{Int64})
   @ Base .\rational.jl:78
 [5] //(x::Int64, y::Complex{Rational{Int64}})
   @ Base .\rational.jl:79
 [6] top-level scope
   @ REPL[420]:1

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 14, 2022, 5:58am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/12 "2022-10-14T05:58:17Z")

</div>

> [@uniment](#):
>
> order to represent `NaN` w

ehhh because

> In computing, _NaN_ standing for Not a Number,

NaN is more or less like a sentinel value to say “hey it’s undefined” without aborting the program, I personally like to imagine if IEEE floating points were designed in an age where error handling is more systematic, it might have been an error.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 14, 2022, 6:01am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/13 "2022-10-14T06:01:49Z")

</div>

> [@uniment](#):
>
> call to `harmonicmean`, but that’s not the point.

but that’s exactly the point? does this bother you?

```julia
julia> f(x) = x
f (generic function with 1 method)

julia> f(0/0)
NaN

julia> f(0÷0)
ERROR: DivideError: integer division error
Stacktrace:
 [1] div(x::Int64, y::Int64)
   @ Base ./int.jl:288
 [2] top-level scope
   @ REPL[3]:1

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 14, 2022, 6:43am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/14 "2022-10-14T06:43:46Z")

</div>

> [@jling](#):
>
> does this bother you?

Hm, never has. I like when languages do the smartest thing possible, but when using the `÷` operator, I do so accepting that integer arithmetic is more limited.

Let me turn the question around on you. Would you propose that all the following six lines should throw errors?

```julia
julia> 1/0
Inf

julia> 0/0
NaN

julia> 1÷0
ERROR: DivideError: integer division error

julia> 0÷0
ERROR: DivideError: integer division error

julia> 1//0
1//0

julia> 0//0
ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64)

```

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [October 14, 2022, 10:37am UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/15 "2022-10-14T10:37:13Z")

</div>

> [@stevengj](#):
>
> It was intentionally allowed in order to represent `Inf` with a `Rational`

[Triva] Yes, I thought so, but unlike in IEEE Inf (only two Inf and -Inf, or for each precision), curously rationals actually have infinitly many representations (well finite for Int64-based, infinite for BigInt-based, or limited by memory). This doesn’t pose any problems:

```julia
julia> 1//0 == 2//0 # both +Inf up to Inf//0
true

```

On the other hand while NaN has many representations in IEEE, it has none in rationals, not even analogous to (I guess not done for rationals for performance reasons):

```julia
julia> 0/0
NaN

```

On top of the many NaN representations in IEEE, it’s in addition signed, by default it’s -NaN and showing as NaN always. I guess -NaN was chosen for sorting reasons, +NaN is:

```julia
julia> bitstring(reinterpret(Float64, reinterpret(Int64, NaN) & (2^63-1)))
"0111111111111000000000000000000000000000000000000000000000000000"

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 14, 2022, 2:04pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/16 "2022-10-14T14:04:12Z")

</div>

> [@Palli](#):
>
> while NaN has many representations in IEEE, it has none in rationals, not even analogous to

it’s not a number… folks, NaN is an “error,” or “exception”, it’s a value because this is the way to handle runtime exception at low level (you can’t throw and catch error at assembly can you).

why would a “error” have a place in our high-level, language-specific rational number system?

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [October 14, 2022, 2:41pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/17 "2022-10-14T14:41:44Z")

</div>

I’m not saying rationals need NaN, I’m very ok with an exception thrown on 0//0. [I would also think it might be ok for 0/0]. To me Inf, and -Inf are also “error conditions”, and seemingly could throw (for rationals, likely would be slower; also for floats). It’s intentional to allow converting Inf and -Inf to rationals, but I at least don’t know it’s very useful? When do you construct Inf intentionally (in floats or rationals)?

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 14, 2022, 2:52pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/18 "2022-10-14T14:52:56Z")

</div>

> [@Palli](#):
>
> When do you construct Inf intentionally (in floats or rationals)?

this could happen

```julia
julia> rationalize(Inf)
1//0

julia> rationalize(NaN)
ERROR: InexactError: Int64(NaN)
Stacktrace:

```

---

<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:** [October 14, 2022, 3:11pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/19 "2022-10-14T15:11:23Z")

</div>

Personally, I don’t hate `0//0` having `NaN`-like properties. I consider `NaN`s to be useful. They allow you complete a whole block of computations before error checking, rather than requiring you to preemptively handle each operation.

There are (were?) processor modes where `NaN`s trigger traps, rather than quietly propagate. It’s just that they are virtually unused because `NaN` propagation works so well and traps are a pain.

> [@Palli](#):
>
> curously rationals actually have infinitly many representations

Rational provides only a single infinity for each sign, just like `IEEE754` floats.

```julia
julia> 2//0
1//0

julia> 1//0 === 2//0
true

```

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [October 14, 2022, 3:11pm UTC](https://discourse.julialang.org/t/surprise-with-rational/88694/20 "2022-10-14T15:11:53Z")

</div>

Right, if we chose so, we could have defined it to throw an exception.

I see now one way to generate Infs in rational calculations, i.e. you divide a non-zero by a zero. We need Inf for rationals in one case. It’s not an argument that checking for zero is going to be slower, since there’s already this (slower) check `iszero(den) && iszero(num) && __throw_rational_argerror_zero(T)` (there’s also Base.checked\_den, I might be wrong).

Infs are never generated for other calculations:

```julia
julia> 9223372036854775807//1 + 1
ERROR: OverflowError: 9223372036854775807 + 1 overflowed for type Int64

julia> BigInt(9223372036854775807)//1 + 1
9223372036854775808//1

```

It seems like we could have thrown, not propagate Infs.

[Next page](https://discourse.julialang.org/t/surprise-with-rational/88694.md?page=2)
