F# Math (I.) - Numeric types in PowerPack
In this article, we'll briefly look at two numeric types that are available in F# PowerPack.
The type complex
represents complex numbers consisting of real and imaginary parts.
Both parts are stored as a floating point numbers. The type BigRational
represents
rational numbers consisting of numerator and denominator of arbitrary sizes. Integers of arbitrary
size are represented using BigInteger
type that is available in .NET 4.0
(in the System.Numerics.dll
assembly). On .NET 2.0, the BigInteger
type is also a part of F# PowerPack.
This article is a part of a series that covers some F# and F# PowerPack features for numerical computing. Other articles in this series discuss matrices, defining custom numeric types and writing generic code. For links to other parts, see F# Math - Overview of F# PowerPack.
Using complex numbers
The first example demonstrates a few basic operations that can be done with complex
numbers in F#. In order to run the code in the listing, you first need to reference
FSharp.PowerPack.dll
. This can be done either using #r
directive in F# Interactive
(when working in an F# Script file such as Script.fsx
) or using the “Add Reference”
dialog in Visual Studio when creating an F# project (or using -r:FSharp.PowerPack.dll
option of the compiler):
1: #r "FSharp.PowerPack.dll" 2: open Microsoft.FSharp.Math;; 3: 4: let c = complex -1.0 0.0;; 5: val c : complex = -1r+0i 6: 7: let i = sqrt c;; 8: val i : Complex = 6.12303176911189e-17r+1i 9: 10: i * i;; 11: val it : Complex = -1r+1.22460635382238e-16i { ... }
After referencing PowerPack and opening the namespace that contains functionality for
mathematical calculations, the snippet creates a number using the complex
function.
The first parameter represents the real part and the second one represents the imaginary part,
so the example creates a complex number representing a real number -1. The next step uses
sqrt
function to calculate the square root. Since the calculation uses complex
numbers, this is a valid operation and it returns the number i (with some rounding error).
Next, the snippet calculates square of i to get a real number -1 again.
Note that both sqrt
and * are standard operations that are available in the F#
library and don’t handle the complex
type explicitly. Instead, they work with any
type that provides specific members. This is done using static member constraints, which are
discussed in the last two articles of this series (see introduction
for links).
Using rational numbers
In this section, we look at a similarly simple listing that shows working with the
BigRational
type. Literals of this type can be written more easily using the
N
suffix. The suffix can be only when the FSharp.PowerPack.dll
library is referenced and it can be used to write rational numbers representing
(arbitrary large) integers. To get a rational representing a fraction, we can use
division operator:
1: let r1 = 4N / 6N;; 2: val r : BigRational = 2/3N 3: 4: let r2 = pown r1 10;; 5: val r : BigRational = 1024/59049N 6: 7: let q1 = pown (1N / 2N) 5 8: let q2 = pown (1N / 2N) 6;; 9: val q1 : BigRational = 1/32N 10: val q2 : BigRational = 1/64N 11: 12: q2 < r2 && q1 > r2;; 13: val it : bool = true
Literals such as 4N
can be only created from integers. However, the overloaded
division operator for BigRational
returns a BigRational
representing
fractions. As you can see, the number is stored in a normalized form (this means there is no
integer such that the numerator and denominator can be divided by it).
The next couple of lines show code that finds a number q such that
(1/2)q is smaller the number r2
and (1/2)(q+1)
is larger. Just like when working with complex numbers, we can use standard functions
and operators such as pown
and <. These are overloaded
and work with any numeric type.
The literals of BigRational
can be constructed using notation such as 4N
.
This is not a built-in language feature. In fact, you can define your own literals (using
a limited set of suffixes) for your own numeric type. This is discussed in an article that
explains how to define a custom numeric type (see introduction
for links).
Calculating PI in F#
To look at some larger example that uses the BigRational
type, let's write an
F# snippet that calculates an estimate of the number π. The sample uses Gregory–Leibniz series
that is explained on WikiPedia [3]. First few members of the series
are 4/1 - 4/3 + 4/5 - 4/7 etc. It is quite easy to see the pattern - the denominator
always increases by 2 and the operation varies between + and -. The series is quite simple to
calculate, but it does not give a very precise estimate. However, it is good enough to
demonstrate working with rational numbers.
We can make the code nicely functional by representing the series as a value of type
seq<BigRational>
. The following series
function calculates
a single element of the series (using denominator n
) and then calls itself
recursively to calculate the rest:
1: /// Generates series that approximates PI 2: /// 4/1 - 4/3 + 4/5 - 4/7 + 4/9 (starting from q * 4/n) 3: let rec series n q = 4: seq { 5: yield q * 4N / n 6: yield! series (n + 2N) (q * -1N) } 7: 8: let pi1 = series 1N 1N |> Seq.take 10 |> Seq.reduce (+) 9: val pi1 : BigRational = 44257352/14549535N 10: float pi1 11: val it : float = 3.041839619 12: 13: let pi2 = series 1N 1N |> Seq.take 300 |> Seq.reduce (+) 14: val pi2 : BigRational = (...) 15: float pi2 16: val it : float = 3.13825933
If we added all elements of the series, we would get the precise value of π.
Unfortunately, we don't have infinite space and time, so the snippet just takes
first few elements (using Seq.take
) and then adds all elements
using Seq.reduce
. Every second element is already negative, because
the series
function multiplies the elements by q
, which
alternates between 1 and -1, so we can use the + operator as the aggregation function.
Using F# numeric types from C#
The PowerPack library is mainly designed as a library with additional functionality for F#. However,
it is a standard .NET library and can be referenced from C# too. Moreover, F# represents overloaded
operators in the same way as C#, so the types BigRational
and Complex
(and also BigInteger
when using .NET 2.0) can be quite nicely used from C#.
The following example re-implements the previous F# snippet to calculate the number π. It follows the
same approach as F#. The method Series
generates elements of the
Gregory–Leibniz series. The implementation uses loop instead of recursion, because recursive
iterators cannot be written efficiently in C#:
static IEnumerable<BigRational> Series() {
var n = BigRational.FromInt(1);
var q = BigRational.FromInt(1);
while (true) {
yield return q * BigRational.FromInt(4) / n;
n = n + BigRational.FromInt(2);
q = q * BigRational.FromInt(-1);
}
}
As you can see, we can create BigRational
from integers using a static method
BigRational.FromInt
. The type also implements standard overloaded operators
(the snippet uses +
, *
and /
) and a few additional operations, such as
BigRational.PowN
(to calculate power). To get approximate value of π as
a floating point number, we can write:
var pi = Series().Take(300).Aggregate((a, b) => a + b);
Console.WriteLine(BigRational.ToDouble(pi));
In C#, we can use LINQ to take 300 elements of the series and sum them. The resulting rational
number can be converted to floating point using BigRational.ToDouble
:
Summary
Using F# PowerPack from C# is just a bonus. The library is mainly designed for F#, but it follows standard .NET programming patterns, so it can be nicely used from C# too. The F# PowerPack implementation of rational numbers is likely good enough for many applications, but if you need more features or better performance, you can check out a list of numerical libraries for .NET (and F#) on MSDN [4].
References & Links
- [1] F# PowerPack (source code and binary packages) - CodePlex
- [2] Numeric Computing - Real World Functional Programming on MSDN
- [3] Pi - Estimating the Value - WikiPedia
- [4] Numerical Libraries for F# and .NET - Real World Functional Programming on MSDN
Full name: Untitled.c
type: complex
implements: System.IComparable
inherits: System.ValueType
val complex : float -> float -> complex
Full name: Microsoft.FSharp.Math.ComplexTopLevelOperators.complex
--------------------
type complex = Complex
Full name: Microsoft.FSharp.Math.complex
type: complex
implements: System.IComparable
inherits: System.ValueType
Full name: Untitled.i
type: Complex
implements: System.IComparable
inherits: System.ValueType
Full name: Microsoft.FSharp.Core.Operators.sqrt
Full name: Untitled.r1
type: BigRational
implements: System.IComparable
Full name: Untitled.r2
type: BigRational
implements: System.IComparable
Full name: Microsoft.FSharp.Core.Operators.pown
Full name: Untitled.q1
type: BigRational
implements: System.IComparable
Full name: Untitled.q2
type: BigRational
implements: System.IComparable
Full name: Untitled.series
Generates szeries that approximates PI
4/1 - 4/3 + 4/5 - 4/7 + 4/9 (starting from q * 4/n)
type: BigRational
implements: System.IComparable
type: BigRational
implements: System.IComparable
val seq : seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
type: seq<'T>
inherits: System.Collections.IEnumerable
Full name: Untitled.pi1
type: BigRational
implements: System.IComparable
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.take
Full name: Microsoft.FSharp.Collections.Seq.reduce
val float : 'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
type: float
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<float>
implements: System.IEquatable<float>
inherits: System.ValueType
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
type: float<'Measure>
implements: System.IComparable
implements: System.IConvertible
implements: System.IFormattable
implements: System.IComparable<float<'Measure>>
implements: System.IEquatable<float<'Measure>>
inherits: System.ValueType
Full name: Untitled.pi2
type: BigRational
implements: System.IComparable
Published: Wednesday, 2 November 2011, 2:34 AM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: c#, functional, f#, math and numerics