Skip to content

Commit

Permalink
added conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
jishnub committed Mar 7, 2020
1 parent 120c3ba commit bd8737b
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MultiplesOfPi"
uuid = "b749d01f-fee9-4313-9f11-89ddf7ea9d58"
authors = ["Jishnu Bhattacharya", "Center for Space Science", "New York University Abu Dhabi"]
version = "0.1.3"
version = "0.2.0"

[compat]
julia = "1"
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ This package introduces the type `PiTimes` that automatically uses the functions

The number `π` is represented as an `Irrational` type in julia, and may be computed to an arbitrary degree of precision. In normal course of events it is converted to a float when it encounters another number, for example `` is computed by converting both `2` and `π` to floats and subsequently carrying out a floating-point multiplication. This is lossy, as both `2` and `π` may be represented with arbitrary precision. This package delays the conversion of the `π` to a float, treating it as a common factor in algebraic simplifications. This limits floating-point inaccuracies, especially if the terms multiplying `π` are exactly representable in binary. As an added advantage, it uses `sinpi` and `cospi` wherever possible to avoid having to convert `π` to a float altogether.

The concept isn't unknown to julia as such, having been [discussed in 2013](https://github.com/JuliaLang/julia/pull/4112#issuecomment-22961778), but I don't know of an implementation.

# Examples

## Arithmetic
Expand Down
34 changes: 26 additions & 8 deletions src/MultiplesOfPi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,17 @@ Base.:(==)(p::PiTimes,::Irrational{:π}) = isone(p.x)
Base.:(==)(::Irrational{:π},p::PiTimes) = isone(p.x)

for T in (:BigFloat,:Float64,:Float32,:Float16)
eval(quote
Base.$T(p::PiTimes) = π*$T(p.x)
end)
@eval Base.$T(p::PiTimes) = π*$T(p.x)
end

for f in (:iszero,:isfinite,:isnan)
eval(quote
Base.$f(p::PiTimes) = $f(p.x)
end)
@eval Base.$f(p::PiTimes) = $f(p.x)
end

# Unfortunately Irrational numbers do not have a multiplicative identity of the same type,
# so we make do with something that works
# NOTE: This will be changed in the next minor release to one(::PiTimes) = true
Base.one(::PiTimes{T}) where {T} = one(PiTimes{T})
Base.one(::Type{PiTimes{T}}) where {T} = one(T)
Base.one(::Type{PiTimes{T}}) where {T} = true

Base.zero(::PiTimes{T}) where {T} = zero(PiTimes{T})
Base.zero(::Type{PiTimes{T}}) where {T} = PiTimes(zero(T))
Expand Down Expand Up @@ -104,4 +99,27 @@ end

Base.:(//)(p::PiTimes,n) = PiTimes(p.x//n)

# Conversion and promotion

for t in (Int8, Int16, Int32, Int64, Int128, Bool, UInt8, UInt16, UInt32, UInt64, UInt128)
@eval Base.promote_rule(::Type{PiTimes{Float16}}, ::Type{PiTimes{$t}}) = PiTimes{Float16}
@eval Base.promote_rule(::Type{PiTimes{$t}},::Type{PiTimes{Float16}}) = PiTimes{Float16}
end

for t1 in (Float32, Float64)
for t2 in (Int8, Int16, Int32, Int64, Bool, UInt8, UInt16, UInt32, UInt64)
@eval begin
Base.promote_rule(::Type{PiTimes{$t1}}, ::Type{PiTimes{$t2}}) = PiTimes{$t1}
Base.promote_rule(::Type{PiTimes{$t2}}, ::Type{PiTimes{$t1}}) = PiTimes{$t1}
end
end
end

Base.promote_rule(::Type{PiTimes{T}}, ::Type{Irrational{:π}}) where {T} = PiTimes{T}
Base.promote_rule(::Type{Irrational{:π}}, ::Type{PiTimes{T}}) where {T} = PiTimes{T}

Base.convert(::Type{PiTimes},x::Real) = PiTimes(x/π)
Base.convert(::Type{PiTimes{T}},x::Real) where {T} = PiTimes{T}(x/π)
Base.convert(::Type{PiTimes{T}},p::PiTimes) where {T} = PiTimes{T}(p.x)

end # module
76 changes: 58 additions & 18 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,11 @@ end

@testset "zero and one" begin
@testset "one" begin
for T in (Float16,Float32,Float64,Int8,Int16,Int32,Int64,Int128)
@test one(PiTimes{T}) === one(T)
@test one(PiTimes{T}(0)) === one(T)
end
for T in (BigInt,BigFloat)
@test one(PiTimes{T}) == one(T)
@test one(PiTimes{T}(0)) == one(T)
for T in (Float16,Float32,Float64,Int8,Int16,Int32,Int64,Int128,BigInt,BigFloat)
@test one(PiTimes{T}) === true
@test one(PiTimes{T}(0)) === true
end
@test one(Pi) == 1
@test one(Pi) === true
end
@testset "zero" begin
@test zero(Pi) === PiTimes(0) == 0
Expand All @@ -57,16 +53,60 @@ end
end

@testset "conversion" begin
@test convert(Float64,PiTimes(1)) Float64(1π)
@test Float64(PiTimes(1)) Float64(1)*π
@test convert(Float64,PiTimes(2)) Float64(2π)
@test Float64(PiTimes(2)) Float64(2)*π
@test convert(BigFloat,PiTimes(1)) BigFloat(1)*π
@test BigFloat(PiTimes(1)) BigFloat(1)*π
@test convert(Float32,PiTimes(1)) Float32(1)*π
@test Float32(PiTimes(1)) Float32(1)*π
@test convert(Float16,PiTimes(1)) Float16(1)*π
@test Float16(PiTimes(1)) Float16(1)*π
@testset "promote_rule" begin
for t in (Int8, Int16, Int32, Int64, Int128, Bool, UInt8, UInt16, UInt32, UInt64, UInt128)
@test promote_rule(PiTimes{Float16},PiTimes{t}) === PiTimes{Float16}
@test promote_rule(PiTimes{t},PiTimes{Float16}) === PiTimes{Float16}
end
for t1 in (Float32, Float64)
for t2 in (Int8, Int16, Int32, Int64, Bool, UInt8, UInt16, UInt32, UInt64)
@test promote_rule(PiTimes{t1},PiTimes{t2}) === PiTimes{t1}
@test promote_rule(PiTimes{t2},PiTimes{t1}) === PiTimes{t1}
end
end
@test promote_rule(PiTimes{Int},Irrational{}) === PiTimes{Int}
@test promote_rule(PiTimes{Float64},Irrational{}) === PiTimes{Float64}
end
@testset "convert" begin
@testset "to float" begin
@test convert(Float64,PiTimes(1)) Float64(1π)
@test Float64(PiTimes(1)) Float64(1)*π
@test convert(Float64,PiTimes(2)) Float64(2π)
@test Float64(PiTimes(2)) Float64(2)*π
@test convert(BigFloat,PiTimes(1)) BigFloat(1)*π
@test BigFloat(PiTimes(1)) BigFloat(1)*π
@test convert(Float32,PiTimes(1)) Float32(1)*π
@test Float32(PiTimes(1)) Float32(1)*π
@test convert(Float16,PiTimes(1)) Float16(1)*π
@test Float16(PiTimes(1)) Float16(1)*π
end
@testset "from real" begin
@test convert(PiTimes{Int},π) === PiTimes(1)
@test convert(PiTimes{Float64},π) === PiTimes(1.0)
@test convert(PiTimes,π) === PiTimes(1.0)

@test convert(PiTimes,2) === PiTimes(2/π)
for t in (Float16,Float32,Float64)
@test convert(PiTimes{t},2) === PiTimes{t}(2/π)
end
convert(PiTimes{BigFloat},2) === PiTimes{BigFloat}(2/π)
end
@testset "PiTimes" begin
for t in (Int8, Int16, Int32, Int64, Int128, Bool, UInt8, UInt16, UInt32, UInt64, UInt128)
@test convert(PiTimes{Float16},PiTimes{t}(0)) === PiTimes(Float16(0))
end
for t1 in (Float32, Float64)
for t2 in (Int8, Int16, Int32, Int64, Bool, UInt8, UInt16, UInt32, UInt64)
@test convert(PiTimes{t1},PiTimes{t2}(0)) === PiTimes(t1(0))
end
end
end
@testset "Complex" begin
@test Complex(Pi,Pi) == Pi + im*Pi
@test Complex(Pi,π) == Pi + im*Pi
@test Complex(π,Pi) == Pi + im*Pi
end
end
end

@testset "arithmetic" begin
Expand Down

0 comments on commit bd8737b

Please sign in to comment.