Julia: Fix pi == one(pi)*pi or update docs for one()

Created on 10 Oct 2020  ·  10Comments  ·  Source: JuliaLang/julia

Based on the discussion in #37931, I have found that pi == one(pi)*pi is broken. However, it should hold according to the documentation of one() function:

    one(x)
    one(T::type)
Return a multiplicative identity for `x`: a value such that
`one(x)*x == x*one(x) == x`.  Alternatively `one(T)` can
take a type `T`, in which case `one` returns a multiplicative
identity for any `x` of type `T`.

If possible, `one(x)` returns a value of the same type as `x`,
and `one(T)` returns a value of type `T`.  However, this may
not be the case for types representing dimensionful quantities
(e.g. time in days), since the multiplicative
identity must be dimensionless.  In that case, `one(x)`
should return an identity value of the same precision
(and shape, for matrices) as `x`.
...

Don't know if you prefer fixing the implementation or just updating the documentation to make it consistent. Notice that since one(pi) already returns true, it would be possible to make true * pi === pi.

Most helpful comment

🤷‍♂️ Really starting to regret the Irrational type entirely.

All 10 comments

FYI: Returning 1.0 (or 1 or true) isn't strictly wrong.

That is the multiplicative identity. The problem comes when you multiply it with pi, or even 2 with pi, you want to get pi or 2pi, but that's only possibly in a CAS (maybe e.g. in http://nemocas.org/ ). It was decided that actualy calculations would convert to floats that make pi rational approximation.

I think in this case the docs is at fault. It should say that identity only holds for T being a concrete type.

edit: I meant to distinguish where a T is "concretely" represented by bits but find it hard to point finger at:
julia> isconcretetype(typeof(pi)) true julia> isbits(pi) true julia> sizeof(Int) 8 julia> sizeof(Irrational{:π}) 0
One way to frame the problem is to say: multiplicative identity of Irrational{:pi} doesn't exist. (even before we talk about if the identity is of the same type or not)

typeof(pi) is a concrete type.

🤷‍♂️ Really starting to regret the Irrational type entirely.

While of course it would be nice, I don't really see why this so badly needs to hold --- many mathematical identities are not true e.g. with floating-point numbers.

At some point, I think we had the property that true*x was just x and false*x should always be zero(x). If we had that then this identity would hold. It would be a bit weird though since true*x and false*x would not be of the same type when x is an irrational. Perhaps that would be ok since it would be very amenable to constant propagation and type analysis.

Perhaps a case where a 0-bit integer is useful!

Isn't pi (and friends) already 0-bit integers? (base pi and whatnot)

First, thank you for the responses. I understand this is a minor issue.

I totally agree with @StefanKarpinski that the whole Irrational type is somehow unfortunate. I can demonstrate it by more practical examples:

1) Problem with negation (not even helps)

julia> x = big(1.0)
1.0
julia> cos(-big(π)-x) == cos(π+x)
true
julia> cos(-π-x) ≈ cos(π+x)
false

2) may look like an irrational constant (but is already converted to Float64), and the order of operations matters

julia> 2π+x == π+π+x
true
julia> 2π+x ≈ π+(π+x)
false

The current documentation contains only the following text, but nothing about Float64 is a fallback type.

  AbstractIrrational <: Real

  Number type representing an exact irrational value, which is automatically
  rounded to the correct precision in arithmetic operations with other numeric
  quantities.

What I love about julia is that it is easy to use and intuitive. But the behavior in these examples is not intuitive.

There is no easy fix. pi is already threatened as Float64 in many cases. Thus, the least breaking change would probably be to make pi behave like Float64 constant in v2.0 with a single exception of big(pi) or BigFloat(pi), respectively. This would also solve this issue since pi == one(pi)*pi would compare two floats. However, it would be necessary to deprecate current arithmetic operations with {Float16, Float32, BigFloat} like

big(1.0) + pi           # deprecate this
big(1.0) + big(pi)      # in favour of this

I belive it would also make user's code less error-prone.

julia> cos(-big(π)-x) == cos(π+x)
true
julia> cos(-π-x) ≈ cos(π+x)
false

The latter says a bit more about how cos is implemented on floats (vs big numbers) rather than about pi.

It is a bit surprising that it's not even approximately true, with cos symmetric and e.g.:

julia> cos(-π-0.1) == cos(π+0.1)
true

Note also that despite:

julia> cos(-big(π)-x) == cos(π+x)
true

Neither side gives the correct value as both are irrational (and the same), there the approximation of is just the same.

You did the right thing with -big(pi), note how careful you would have to be:

julia> -big(pi) ≈ big(-pi)
false
Was this page helpful?
0 / 5 - 0 ratings

Related issues

wilburtownsend picture wilburtownsend  ·  3Comments

tkoolen picture tkoolen  ·  3Comments

i-apellaniz picture i-apellaniz  ·  3Comments

arshpreetsingh picture arshpreetsingh  ·  3Comments

dpsanders picture dpsanders  ·  3Comments