Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement tau==2pi (in type-stable manner) #28

Closed
wants to merge 8 commits into from
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ After installing this package with `Pkg.add("Tau")`, it can be used as follows:
```julia
using Tau

tau == τ 2*pi # => true
typeof(tau) # => Irrational{:τ}
tau == τ == 2*pi # => true
typeof(tau) # => Irrational{:τ}
```

Note: to input the τ character, type `\tau` then press <kbd>Tab</kbd>.
Expand All @@ -40,11 +40,3 @@ The tau variants of `sinpi`, `cospi`, and `mod2pi` are also defined:
sintau(1//4) # => 1.0
costau(1//2) # => -1.0
```

## The tau != 2pi inequality

When this package was first created, the equality `tau == 2pi` did hold true,
in accordance to the mathematical definition of the constant.
However, that is not valid anymore -- the two values are only approximately equal: `tau ≈ 2*pi`.

For a detailed explanation of the reasons for this, see [this document](tau-2pi-equality.md).
17 changes: 17 additions & 0 deletions src/Tau.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ export tau, τ,
Base.@irrational τ 6.28318530717958647692 (2 * big(pi))
const tau = τ

# Implement tau == 2pi (and pi = tau/2) by defining additional signatures for the == method.
# This overrides, for these cases only, the (always false) result
# of `==(::Irrational, ::Real)` defined in base/irrationals.jl
import Base.==
=={T<:Real}(::Irrational{:τ}, n::T) = n == 2 * T(π)
=={T<:Real}(n::T, ::Irrational{:τ}) = n == 2 * T(π)
=={T<:Real}(::Irrational{:π}, n::T) = n == T(τ) / 2
=={T<:Real}(n::T, ::Irrational{:π}) = n == T(τ) / 2
# Note: we're using the method signature syntax `foo{T<:Real}(x::T) = ...` that's valid in julia 0.5,
# instead of the new `foo(x::T) where T<:Real = ...` syntax introduced in julia 0.6,
# to avoid breaking backward compatibility or introducing a dependency on Compat.jl

# Disambiguate the `==(:`Irrational{s}, ::Irrational{s}) where {s}`
# defined in base/irrationals.jl
==(::Irrational{:τ}, ::Irrational{:τ}) = true
==(::Irrational{:π}, ::Irrational{:π}) = true

include("trig.jl")

modtau(x) = Base.mod2pi(x)
Expand Down
10 changes: 8 additions & 2 deletions tau-2pi-equality.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
When this package was first created, the equality `tau == 2pi` did hold true,
in accordance to the mathematical definition of the constant.
However, that is not valid anymore -- the two values are now only approximately equal, i.e. `tau ≈ 2*pi`.
However, for a while that wasn't the case
-- i.e. the two values were only approximately equal, i.e. `tau ≈ 2*pi`.
This document explains in detail the reasons for the inequality.

Both `pi` and `tau` are defined as instances of the type `Irrational`,
Expand Down Expand Up @@ -37,7 +38,12 @@ A prime example is the non-checking of integer overflow, which often surprised u
(see the links in [JuliaLang/julia#2085][2085])
so much so that it has led to [a dedicated FAQ entry][FAQ].

So the reason for `tau != 2pi` is that, in the computing sense, they are represented by different structures in Julia.
Although the general reasoning for `Irrational`s to be strictly unequal to any other number remains valid,
this package provides a special case for the `tau == 2pi` equality, since that's true *by definition*.
This is achieved by specifying additional function signatures for the `==` method.
Doing `==(tau, 2pi) = true` rather than `*(2, pi) = tau` (dummy code, simplified for clarity)
prevents breaking type stability, which would be an issue if the `*` function returned
an Irrational when called with the parameters `2` and `pi`, and Float64 for all other cases.

[3316]: https://github.com/JuliaLang/julia/pull/3316
[9198]: https://github.com/JuliaLang/julia/pull/9198
Expand Down
52 changes: 41 additions & 11 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,58 @@ using Tau
using Base.Test

@testset "self-identity" begin
@test isa(tau, Irrational)
@test τ == τ
@test τ == tau

@testset "tau" begin
@test isa(tau, Irrational)
@test τ == τ
@test τ === τ
@test τ == tau
@test τ === tau
@test τ == 1τ
@test τ !== 1τ
@test τ == big(tau)
@test τ !== big(tau)
end

# Also test pi self-identity, for 100% test coverage
@testset "pi" begin
@test π == π
@test π === π
@test π == pi
@test π === pi
@test π == 1π
@test π !== 1π
@test π == big(pi)
@test π !== big(pi)
end

end

@testset "tau vs. 2pi" begin

@testset "symbols" begin
@test τ # tau is Irrational, can't be equal to an AbstractFloat
@test 2π τ
@test π τ/2
@test τ/2 π
@test τ ==
@test 2π == τ
@test π == τ/2
@test τ/2 == π
end

@testset "ascii" begin
@test tau ≠ 2*pi # tau is Irrational, can't be equal to an AbstractFloat
@test 2*pi ≠ tau
@test pi ≠ tau/2 # pi is Irrational, can't be equal to an AbstractFloat
@test tau/2 ≠ pi
@test tau == 2*pi
@test 2*pi == tau
@test pi == tau/2
@test tau/2 == pi
end

@testset "arithmetic operations" begin
@test pi == 0.5 * tau
@test tau == pi + pi
@test pi == tau - pi
@test 4pi == 2tau
end

@testset "explicit type conversions" begin
@test tau == 2 * BigFloat(pi)
@test Float32(tau) == 2 * Float32(pi)
@test Float64(Float32(tau)) == Float64(2 * Float32(pi))
@test BigFloat(tau) == 2 * BigFloat(pi)
Expand Down