Arbitrary-precision numbers
Rug provides integers and floating-point numbers with arbitrary precision and correct rounding. Its main features are
- bignum integers with arbitrary precision,
- bignum rational numbers with arbitrary precision,
- multi-precision floating-point numbers with correct rounding, and
- multi-precision complex numbers with correct rounding.
This crate is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See the full text of the GNU LGPL and GNU GPL for details.
Version 1.0.0 coming soon
Version 0.10.0 is meant to be the last release before version 1.0.0. Unless some issue is discovered, version 1.0.0 will be like version 0.10.0 with all the deprecated items removed.
Getting started
Setting up the crate
To use Rug in your crate, add it as a dependency inside Cargo.toml:
[]
= "0.10.0"
This crate depends on the low-level bindings in the gmp-mpfr-sys crate which needs some setup to build; the gmp-mpfr-sys documentation has some details on usage under GNU/Linux, macOS and Windows.
You also need to declare the crate by adding this to your crate root:
extern crate rug;
More details are available in the Crate usage section below.
Quick example
For many operations, you can use the arbitrary-precision types such as
Integer
like you use primitive types such as
i32
. The main difference is that the Rug types do not
implement Copy
. This is because they store their digits
in the heap, not on the stack, and copying them could involve an
expensive deep copy.
This code uses the Integer
type:
extern crate rug;
use ;
Some points from this example follow:
Integer::new()
creates a newInteger
intialized to zero.- To assign values to Rug types, we use the
Assign
trait and its methodassign
. We do not use the assignment operator=
as that would move the left-hand-side operand, which would have the same type. - Arbitrary precision numbers can hold numbers that are too large to
fit in a primitive type. To assign such a number to the large types,
we use strings rather than primitives; in the example this is done
using both
Integer::parse
andInteger::parse_radix
. - We can compare Rug types with primitive types or with other Rug
types using the normal comparison operators, for example
int > 100_000_000_000
. - Most arithmetic operations are supported with Rug types and
primitive types on either side of the operator, for example
int >> 128
.
Using primitive types
With Rust primitive types, arithmetic operators usually operate on two
values of the same type, for example 12i32 + 5i32
. Unlike primitive
types, conversion to and from Rug types can be expensive, so the
arithmetic operators are overloaded to work on many combinations of
Rug types and primitives. The following are provided:
- Where they make sense, all arithmetic operators are overloaded to
work with Rug types and the primitives
i32
,u32
,f32
andf64
. - Where they make sense, conversions using the
From
trait and assignments using theAssign
trait are supported for all the primitives in 1 above as well as the other primitivesi8
,i16
,i64
,isize
,u8
,u16
,u64
andusize
. - Comparisons between Rug types and all the primitives listed in 1 and 2 above are supported.
- For
Rational
numbers, operations are also supported for tuples containing two primitives: the first is the numerator and the second is the denominator which cannot be zero. The two primitives do not need to have the same type. - For
Complex
numbers, operations are also supported for tuples containing two primitives: the first is the real part and the second is the imaginary part. The two primitives do not need to have the same type.
Operators
Operators are overloaded to work on Rug types alone or on a combination of Rug types and Rust primitives. When at least one operand is an owned Rug type, the operation will consume that type and return a Rug type. For example
use Integer;
let a = from;
let b = 5 - a;
assert_eq!;
Here a
is consumed by the subtraction, and b
is an owned
Integer
.
If on the other hand there are no owned Rug types and there are references instead, the returned value is not the final value, but an incomplete computation value. For example
use Integer;
let = ;
let incomplete = &a - &b;
// This would fail to compile: assert_eq!(incomplete, -10);
let sub = from;
assert_eq!;
Here a
and b
are not consumed, and incomplete
is not the final
value. It still needs to be converted or assigned into an
Integer
. The reason is explained in the section about
incomplete computation values.
The left shift <<
and right shift >>
operators support shifting by
negative values, for example a << 5
is equivalent to a >> -5
. The
shifting operators are also supported for the Float
and
Complex
types, where they are equivalent to
multiplication or division by a power of two.
Incomplete computation values
There are two main reasons why operations like &a - &b
do not
perform a complete computation and return a Rug type:
- Sometimes we need to assign the result to an object that already exists. Since Rug types require memory allocations, this can help reduce the number of allocations.
- For the
Float
type, we need to know the precision when we create a value, and the operation itself does not convey information about what precision is desired for the result. The same holds for theComplex
type.
There are two things that can be done with incomplete computation values:
- Assign them to an existing object without unnecessary allocations.
This is usually achieved using the
Assign
trait or a similar method, for exampleint.assign(incomplete)
andfloat.assign_round(incomplete, Round::Up)
. - Convert them to the final value using the
From
trait or a similar method, for exampleInteger::from(incomplete)
andFloat::with_val(53, incomplete)
.
Let us consider a couple of examples.
use ;
let mut buffer = new;
// ... buffer can be used and reused ...
let = ;
let incomplete = &a - &b;
buffer.assign;
assert_eq!;
Here the assignment from incomplete
into buffer
does not require
an allocation unless the result does not fit in the current capacity
of buffer
. And even then, the reallocation would take place before
the computation, so no copies are involved. If &a - &b
returned an
Integer
instead, then an allocation would take place even
if it is not necessary.
use Float;
use Constant;
// x has a precision of 10 bits, y has a precision of 50 bits
let x = with_val;
let y = with_val;
let incomplete = &x / &y;
// z has a precision of 45 bits
let z = with_val;
assert!;
The precision to use for the result depends on the requirements of the
algorithm being implemented. Here c
is created with a precision of
45.
In these two examples, we could have left out the incomplete
variables altogether and used buffer.assign(&a - &b)
and
Float::with_val(45, &x / &y)
directly.
Many operations can return incomplete computation values:
- unary operators applied to references, for example
-&int
; - binary operators applied to two references, for example
&int1 + &int2
; - binary operators applied to a primitive and a reference, for example
&int * 10
; - methods that take a reference, for example
int.abs_ref()
; - methods that take two references, for example
int1.div_rem_ref(&int2)
; - string parsing, for example
Integer::parse("12")
; - and moreā¦
These operations return objects that can be stored in temporary
variables like incomplete
in the last few examples. However, the
names of the types are not public, and consequently, the incomplete
computation values cannot be for example stored in a struct. If you
need to store the value in a struct, convert it to its final type and
value.
Crate usage
Rug requires rustc version 1.18.0 or later.
This crate depends on the low-level bindings in the gmp-mpfr-sys crate, which provides Rust FFI bindings for:
- the GNU Multiple Precision Arithmetic Library (GMP),
- the GNU MPFR Library, a library for multiple-precision floating-point computations, and
- GNU MPC, a library for the arithmetic of complex numbers with arbitrarily high precision.
It can be helpful to refer to the documentation of the gmp-mpfr-sys crate and of the GMP, MPFR and MPC libraries.
Optional features
The Rug crate has six optional features:
integer
, enabled by default. Required for theInteger
type and its supporting features.rational
, enabled by default. Required for theRational
type and its supporting features. This feature requires theinteger
feature.float
, enabled by default. Required for theFloat
type and its supporting features.complex
, enabled by default. Required for theComplex
type and its supporting features. This feature requires thefloat
feature.rand
, enabled by default. Required for theRandState
type and its supporting features. This feature requires theinteger
feature.serde
, disabled by default. This provides serialization support for theInteger
,Rational
,Float
andComplex
types, providing that they are enabled. This feature requires the serde crate.
The first five optional features are enabled by default; to use features selectively, you can add the dependency like this to Cargo.toml:
[]
= "0.10.0"
= false
= ["integer", "float", "rand"]
Here only the integer
, float
and rand
features are enabled. If
none of the features are selected, the gmp-mpfr-sys crate is
not required and thus not enabled. In that case, only the
Assign
trait and some other traits are
provided by the crate.