# Zero(π) and one(π) are false and true?

**URL:** <https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802>\
**Category:** New to Julia\
**Tags:** numbers\
**Created:** [September 26, 2022, 8:10am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802 "2022-09-26T08:10:46Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [September 26, 2022, 8:10am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/1 "2022-09-26T08:10:46Z")

</div>

The code for irrationals.jl in base defines:

```julia
zero(::AbstractIrrational) = false
zero(::Type{<:AbstractIrrational}) = false

one(::AbstractIrrational) = true
one(::Type{<:AbstractIrrational}) = true

```

So, then, for example:

```julia
julia> zero(π)
false

julia> one(π)
true

```

This was unexpected.

What is the reasoning behind this choice?

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [September 26, 2022, 8:23am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/2 "2022-09-26T08:23:45Z")

</div>

`AbstractIrrational <: Real`, so `zero` and `one` should return some kind of real number with the numerical value 0 and 1.

The docs of `false` states:

```julia
Moreover, false acts as a
  multiplicative "strong zero":

  julia> false == 0
  true

  julia> true == 1
  true

  julia> 0 * NaN
  NaN

  julia> false * NaN
  0.0

```

It’s likely that is the motivation for returning `Bool` values.

---

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [September 26, 2022, 8:57am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/3 "2022-09-26T08:57:24Z")

</div>

I think that this result is not correct. According to the docs, `zero(T::Type)` and `one(T::Type`) return the additive and multiplicative identities for `T`, respectively. But in this case that’s not true:

```julia
julia> zero(π) + π == π
false

julia> one(π) * π == π
false

```

---

<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 26, 2022, 9:31am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/4 "2022-09-26T09:31:19Z")

</div>

> [@heliosdrm](#):
>
> But in this case that’s not true:

That is expected - julia is not a CAS and trying to preserve the `Irrational` types in particular quickly leads to trying to make julia one.

The particular choice here is also intentional, see `@edit ==((zero(π) + π), π)`:

```julia
# Irrationals, by definition, can't have a finite representation
# equal them exactly
==(x::AbstractIrrational, y::Real) = false
==(x::Real, y::AbstractIrrational) = false

```

since mathematical operations on irrationals lead to them being “`Real`ized” to the corresponding precision.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 26, 2022, 10:08am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/5 "2022-09-26T10:08:39Z")

</div>

To be clear you could add methods like

```julia
julia> Base.:+(x::Bool, y::Irrational) = x ? 1 + y : y

julia> false + π === π
true

```

to make `Irrational`s behave a little better.

But that’s the problem with `Irrational`, there’s always something that could be improved a little bit. Maybe you want a specialized method to make `sin(π)` return 0 instead of 1.2246467991473532e-16? That’s easy to add as well. What about `sin(2π)`, maybe not as easy but doable with some more type tricks. Continuing along this road leads you to a full blown Computer Algebra System, which would be completely unrealistic to build into Julia Base.

---

<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:** [September 26, 2022, 10:12am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/6 "2022-09-26T10:12:24Z")

</div>

Actually:

```julia
julia> zero(π) + π ≈ π
true

julia> 0.0 + π == π
false

julia> one(π) * π ≈ π
true

```

> [@jakobnissen](#):
>
> ```julia
> Moreover, false acts as a multiplicative "strong zero":
> 
> [..]
> 
> julia> false * NaN
> 0.0
> 
> ```

That’s interesting, is that a warning, or is this wanted?

> [@GunnarFarneback](#):
>
> Maybe you want a specialized method to make `sin(π)` return 0 instead of 1.2246467991473532e-16?

Is that from some (older?) Julia version? Julia 1.8.1 (for me, and should for all, I believe no portability issue) DOES return 0.0 exactly (I think they tried reall hard for that to happen unlike for other multiples of π). But not for `bitstring(sin(2π))`, nor even sin(-π), does for sin(0) though. There’s a reason for `sinpi`.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [September 26, 2022, 10:13am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/7 "2022-09-26T10:13:15Z")

</div>

But if

```julia
julia> zero(π) + π == π
false

```

then there’s an argument that either this should be fixed, or methods for `one` and `zero` should be removed.

There may be a blurry line somewhere between what should be included and what shouldn’t, but I don’t think that line should lie _between_ the existence of `zero(::AbstractIrrational)` and the correct behavior of `zero(π) + π`.

---

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [September 26, 2022, 10:25am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/8 "2022-09-26T10:25:17Z")

</div>

I found this related issue, in case this conversation leads to something actionable:

> <https://github.com/JuliaLang/julia/issues/34003>
>
> (This issue is a follow-up to #34000 #28854 #31303)
> 
> The docstring for \`zero(x…)\` says
> 
> \> Get the additive identity element for the type of x
> 
> However, defining this additive identity precisely seems to be confusing, especially for union types, and in the presence of type promotion rules. The rest of this issue is dedicated to exploring more precisely what the semantics of \`zero(T)\` is (to help figure out what exactly the semantics should be).
> 
> 1. In many situations, \`zero(::Union)\` is undefined when it could be defined. Intuitively, one might expect that the presence of multiple zeros might complicate the definition. However, type promotion seems to allow for a uniquely defined result. If we take the definition above seriously, the statements
> 
> \`\`\`julia
> julia\> Float16(0.0) + Float32(1.0) === Float32(1.0) #works for Float32
> true
> 
> julia\> Float16(0.0) + Float16(1.0) === Float16(1.0) #and for Float16
> true
> \`\`\`
> 
> would argue that \`zero(Union{Float16,Float32}) === Float16(0.0)\` (it is currently undefined).
> 
> 2. A counterargument to the above would be that \`Int(0)\` is also a valid choice for \`zero(Union{Float16,Float32})\`, since
> 
> \`\`\`julia
> julia\> 0 + Float16(1.0) === Float16(1.0) # works because of type promotion
> true
> \`\`\`
> 
> However, the statement above would also imply that \`Int(0)\` is a valid choice for \`zero(Float16)\`.
> We can exclude this value by requiring \`zero()\` to be endomorphic, i.e. \`zero(::T) :: T\`, and by extension \`zero(T) :: T\`.
> 
> 3. \`Int(0)\` appears to be the result of \`zero(T)\` whenever\`T \<: Union{Missing,Number,Complex}\`, even though it is not always the unique (or even correct) additive identity in \`T\`:
> 
> \`\`\`
> julia\> zero(Union{Int,Complex})
> 0
> 
> julia\> zero(Union{Real,Complex}) #due to type promotion, see #2
> 0
> 
> julia\> 0 + Complex{Bool}(0) === Complex{Bool}(0) # Counterexample: actually Complex{Int}(0), so Int(0) is not an additive identity over Complex
> false
> 
> julia\> Int(0) + Int16(0) === Int(0) #Counterexample: Int16 \<: Real but adding Int(0) to it promotes to Int
> true
> 
> julia\> Int(0) + false === Int(0) #Counterexample: Bool \<: Real but adding Int(0) to it promotes to Int
> true
> \`\`\`
> 
> \---
> 
> \## Proposal
> 
> 1. \`zero(T)\` for non-union types should be defined as the unique additive identity such that
> a. \`zero(T)::T\`, i.e. \`zero()\` is an endomorphism, and
> b. The identity \`zero(T) + zero(T) === zero(T)\` is satisfied for all \`T\`.
> 
> 2. \`zero(Union{T,S}) === zero(T)\` when \`zero(T) + zero(S) === zero(S)\`, taking into account type promotion rules.
> 
> 3. If no unique endomorphic zero exists, then \`zero(T)\` should be undefined, except for \`zero(Bool) === false\` as a special case.
> 
> This definition would preserve the endomorphic property for leaf types, like \`zero(Float16) === Float16(0.0)\`.
> 
> This definition would preserve the result for \`zero(Bool) === false\`, to be consistent with current arithmetic semantics of \`Bool\`. 
> 
> This definition would preserve the semantics of #28854 for \`zero(Missing) === missing\`.
> 
> This definition would define \`zero(Union{Float16,Float32,Float64})\` to be \`Float16(0.0)\` (currently undefined).
> 
> This definition would define \`zero(Union{Float16,Int16})\` to be \`Int16(0)\` (currently undefined).
> 
> This definition would \*change\* \`zero(Real)\` to be \`false\` (and similarly in Case 3 above; currently \`Int(0)\`); arguably a bug fix.
> 
> This definition would \*change\* \`zero(Union{Bool,Int})\` to be \`false\` (currently \`Int(0)\`); arguably a bug fix.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 26, 2022, 10:27am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/9 "2022-09-26T10:27:30Z")

</div>

> [@Palli](#):
>
> > [@jakobnissen](#):
> >
> > ```julia
> > Moreover, false acts as a multiplicative "strong zero":
> > 
> > [..]
> > 
> > julia> false * NaN
> > 0.0
> > 
> > ```
> 
> That’s interesting, is that a warning, or is this wanted?

That’s very much intentional, see the bottom of section [Mathematical Operations and Elementary Functions · The Julia Language](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Arithmetic-Operators).

> [@Palli](#):
>
> > [@GunnarFarneback](#):
> >
> > Maybe you want a specialized method to make `sin(π)` return 0 instead of 1.2246467991473532e-16?
> 
> Is that from some (older?)

Yes, I was still on 1.7. Apparently it was decided to implement exactly that for 1.8:

```julia
julia> @which sin(π)
sin(::Irrational{:π}) in Base.MathConstants at mathconstants.jl:126

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 26, 2022, 10:30am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/10 "2022-09-26T10:30:08Z")

</div>

> [@DNF](#):
>
> There may be a blurry line somewhere between what should be included and what shouldn’t, but I don’t think that line should lie _between_ the existence of `zero(::AbstractIrrational)` and the correct behavior of `zero(π) + π`.

I don’t have a strong opinion on the line for irrationals, but I suspect that removing `zero` and `one` would break a lot of things in the presence of irrationals.

---

<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:** [September 26, 2022, 10:41am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/11 "2022-09-26T10:41:05Z")

</div>

> [@GunnarFarneback](#):
>
> Apparently it was decided to implement exactly that for 1.8

Yes, but I thought the polynominal might have been changed, but apparently not:

```julia
julia> sin(Float64(π))
1.2246467991473532e-16

So just making sure for:

Base.sin(::Irrational{:π}) = 0.0
Base.cos(::Irrational{:π}) = -1.0
Base.sincos(::Irrational{:π}) = (0.0, -1.0)
Base.tan(::Irrational{:π}) = 0.0
Base.cot(::Irrational{:π}) = -1/0

unlike for:
julia> cos(Float64(π)) #even exact with: bitstring(cos(Float16(π)))
-1.0

julia> typeof(-π) # so not possible for it currently (or would there have been some possible workaround to get sin(-π) exact?)
Float64

```

---

<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:** [September 26, 2022, 10:56am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/12 "2022-09-26T10:56:02Z")

</div>

> [@GunnarFarneback](#):
>
> What about `sin(2π)`, maybe not as easy but doable with some more type tricks.

There’s already:

> **[GitHub - JuliaMath/Tau.jl: A Julia module providing the definition of the...](https://github.com/JuliaMath/Tau.jl)**
>
> A Julia module providing the definition of the circle constant Tau (2π) - GitHub - JuliaMath/Tau.jl: A Julia module providing the definition of the circle constant Tau (2π)

I think 2π (as a literal constant) could have been changed to a separate “2π” irrational, and made exact, maybe not good under that name, since variables/constants cant start with number, rather twopi, or simply tau. I think they simply (wisely) chose to not do it in Julia, since can be done in a package; it was maybe a mistake to include any irrationals in Julia, this is a can of worms, and Julia itself doesn’t need them.

Unary minus on irrationals could also have been defined on irrationals, to give their mirrors, and then you have at least 4 pi-related irrationals. At some point it will be surprising, e.g. that 3π or simply 2.0π. So you would have to multiply again by 2 (at least), the number of irrational types, to support float (well Float64 only).

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [September 26, 2022, 11:24am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/13 "2022-09-26T11:24:29Z")

</div>

> [@GunnarFarneback](#):
>
> I suspect that removing `zero` and `one` would break a lot of things in the presence of irrationals.

Naturally, removing `one` and `zero` is not an option. But handling `*(::Bool, ::AbstractIrrational)`, etc., seems like a decent possibility, despite the ‘slippery slope’ argument (I actually think the slippery part lies _between_ those two options). Unless, of course, that too would break stuff.

---

<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:** [September 26, 2022, 11:29am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/14 "2022-09-26T11:29:24Z")

</div>

> [@GunnarFarneback](#):
>
> That’s very much intentional, see the bottom of section [Mathematical Operations and Elementary Functions · The Julia Language](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Arithmetic-Operators).

Thanks for the Knuth reference (just adds one more obscure trivia you need to know for [IEEE] floating point; not sure it applies to Post floating point, and its NaR).

Since false as “strong zero” is meant to disable propagation of NaN, when intended, explicitly used, is it then a bug related to irrationals (false and true, had other reasons for them there, related to Float64 or no other type good enough), to silently disable NaN propagation (which is a good thing, except when you want to turn it off)?

```julia
julia> faraway_calculation_results_in = NaN

julia> faraway_calculation_results_in*zero(π) # not sure how often this, or similar, would be done in practice though.
0.0

unlike:
julia> faraway_calculation_results_in*zero(Float64(π))
NaN

```

[File a (separate) bug? Or is it simply a known issue, part of the other open bug linked from this thread?]

This might have to do with it (didn’t look into it carefully):

> **[GitHub - perrutquist/Zeros.jl: Julia types that represent the numbers zero...](https://github.com/perrutquist/Zeros.jl)**
>
> Julia types that represent the numbers zero and one. - GitHub - perrutquist/Zeros.jl: Julia types that represent the numbers zero and one.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [September 26, 2022, 11:35am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/15 "2022-09-26T11:35:56Z")

</div>

Isn’t `one(π) * π != π` a _bona fide_ bug, then?

```julia
help?> one
 Return a multiplicative identity for x: a value such that one(x)*x == x*one(x) == x.

```

It’s not just a question of improving numerical precision, the _definitions_ of `one` and `zero` are that they should be identities.

---

<div class="post-metadata">

**Author:** ![albheim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albheim/32/34660_2.png) [@albheim](https://discourse.julialang.org/u/albheim)\
**Post date:** [September 26, 2022, 11:58am UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/16 "2022-09-26T11:58:55Z")

</div>

Couldn’t there be an `Irrational{true/false}` representing this?

Some more methods would be needed to make it work properly, but something like this could be one way to handle zero/one identities?

```julia
julia> Base.zero(::AbstractIrrational) = Irrational{false}()

julia> Base.zero(::Type{<:AbstractIrrational}) = Irrational{false}()

julia> Base.one(::AbstractIrrational) = Irrational{true}()

julia> Base.one(::Type{<:AbstractIrrational}) = Irrational{true}()

julia> Base.:+(a::Irrational{false}, b::AbstractIrrational) = b

julia> Base.:+(a::AbstractIrrational, b::Irrational{false}) = b

julia> Base.:*(a::Irrational{true}, b::AbstractIrrational) = b

julia> Base.:*(a::AbstractIrrational, b::Irrational{true}) = b

julia> Base.Float64(::Irrational{false}) = zero(Float64)

julia> Base.Float64(::Irrational{true}) = one(Float64)

julia> zero(π) + π == π
true

julia> one(π) * π == π
true

julia> one(π) + π == π
false

julia> zero(π) * π == π
false

```

---

<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:** [September 26, 2022, 12:06pm UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/17 "2022-09-26T12:06:36Z")

</div>

> Isn’t `one(π) * π != π` a _bona fide_ bug, then?

No, or this is tricky, where is the bug? one(π) == true (meaning “1”) IS the multiplicative identity of π, just regular math).

The “bug” if you will then happens with `true * π` no longer being an irrational (also for `false`). That (and for false) seemingly could be fixed, but not for `true + π` (while `false + π` could be made to work).

```julia
julia> one(π) * π ≉ π # EDIT: !(one(π) * π ≈ π) # I didn't locate an \notapprox operator, is there one?
false

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 26, 2022, 12:08pm UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/18 "2022-09-26T12:08:37Z")

</div>

> [@Palli](#):
>
> `I didn't locate an \notapprox operator`

`\napprox`

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [September 26, 2022, 12:09pm UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/19 "2022-09-26T12:09:25Z")

</div>

> [@Palli](#):
>
> No, or this is tricky, where is the bug.

Well, I would say that if `true` isn’t the multiplicative identity of pi, then `one(pi)` shouldn’t return `true`. So the bug is in `one`.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [September 26, 2022, 12:15pm UTC](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802/20 "2022-09-26T12:15:55Z")

</div>

I think these issues come down to two basic issues:

1. Comparing real numbers for equality is undecidable
2. Implementing a CAS is not in scope for Julia Base

Perhaps Irrational shouldn’t ever have been a type, instead a better design (given the above limitations and expectations of consistency with the rest of Julia) might have been to make each irrational constant a function that takes a `<:Real` type.  
Something like this:

```julia
irrational_pi(Float64) # evaluates to 3.1...

```

or this:

```julia
irrational(:pi, Float64) # evaluates to 3.1...

```

[Next page](https://discourse.julialang.org/t/zero-and-one-are-false-and-true/87802.md?page=2)
