IrrationalExpressions is a Julia module that makes expressions like 2π
behave as irrational numbers, rather than Float64
.
Julia has a few irrational constants, like π
and e
. Arbitrary precision packages, like BigFloat, may provide conversion methods that yield these constants with the desired precision. However, any arithmetic operation that happens before conversion defaults to Float64
.
julia> BigFloat(π) + BigFloat(-π)
1.224646799147353177226065932275001058209749445923078164062861980294536250318213e-16
julia> typeof(-π)
Float64
This may lead to subtle bugs. For example, 2π*x
will only be correct to about 16 decimal places, even if x
has higher precision. It must be written as 2(π*x)
in order to get the precision of x
.
Using the IrrationalExpressions
module, arithmetic operations don't immediately force conversion to Float64. Instead the expression is kept unevaluated until the target type is known.
julia> using IrrationalExpressions
julia> -pi
-π = -3.1415926535897...
julia> BigFloat(π) + BigFloat(-π)
0.0
+
, -
, *
and /
are currently supported.
Downconversion occurs when a floating point value is encountered. The resulting type is that of the floating point value.
julia> τ = 2π
2π = 6.2831853071795...
julia> τ + 0.0
6.283185307179586
julia> τ + BigFloat(0)
6.283185307179586476925286766559005768394338798750211641949889184615632812572396
Functions in Base
typically convert to Float64
when encountering an unknown subtype of Real
. They will work as usual.
julia> cos(2π)
1.0
julia> typeof(ans)
Float64
Integer
, Rational
and Irrational
can be used to build irrational expressions.
Care must be taken so that floats are not accidentally created. (1//2)π
is an IrrationalExpr
, but (1/2)π
is a Float64
.
New floating-point types need not explicitly support conversion from IrrationalExpr
.
Any subtype of AbstractFloat
that has conversions from Integer
, Rational
and Irrational
, promotion from Real
and the necessary arithmetic operations is automatically supported.
Because this is a quick hack, there's no simplification, or elimination of common subexpressions. If irrational expressions are inadvertently created in a loop, they can grow exponentially
julia> a = π; for i=1:5; global a = a-a/3; end; a
((((π - π / 3) - (π - π / 3) / 3) - ((π - π / 3) - (π - π / 3) / 3) / 3) - (((π - π / 3) - (π - π / 3) / 3) - ((π - π / 3) - (π - π / 3) / 3) / 3) / 3) - ((((π - π / 3) - (π - π / 3) / 3) - ((π - π / 3) - (π - π / 3) / 3) / 3) - (((π - π / 3) - (π - π / 3) / 3) - ((π - π / 3) - (π - π / 3) / 3) / 3) / 3) / 3 = 0.41370767454680...
(The work-around is to convert to the desired floating-point type before entering the loop.)
- There needs to be some way to keep expressions from blowing up in a loop. At minimum, the size should be tracked, and an error thrown at some point.
- It would be possible to extend this to things like
sqrt(Integer)
,Integer^Rational
, etc. - Support for complex-valued irrational expressions, like
pi * im
is still missing.