Skip to main content
Version: v0.22.0

Integers

An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types.

info

When an integer is defined in Noir without a specific type, it will default to Field.

The one exception is for loop indices which default to u64 since comparisons on Fields are not possible.

Unsigned Integers

An unsigned integer type is specified first with the letter u (indicating its unsigned nature) followed by its bit size (e.g. 8):

fn main() {
let x: u8 = 1;
let y: u8 = 1;
let z = x + y;
assert (z == 2);
}

The bit size determines the maximum value the integer type can store. For example, a u8 variable can store a value in the range of 0 to 255 (i.e. 281\\2^{8}-1\\).

Signed Integers

A signed integer type is specified first with the letter i (which stands for integer) followed by its bit size (e.g. 8):

fn main() {
let x: i8 = -1;
let y: i8 = -1;
let z = x + y;
assert (z == -2);
}

The bit size determines the maximum and minimum range of value the integer type can store. For example, an i8 variable can store a value in the range of -128 to 127 (i.e. 27\\-2^{7}\\ to 271\\2^{7}-1\\).

tip

If you are using the default proving backend with Noir, both even (e.g. u2, i2) and odd (e.g. u3, i3) arbitrarily-sized integer types up to 127 bits (i.e. u127 and i127) are supported.

Overflows

Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove:

fn main(x: u8, y: u8) {
let z = x + y;
}

With:

x = "255"
y = "1"

Would result in:

$ nargo prove
error: Assertion failed: 'attempt to add with overflow'
┌─ ~/src/main.nr:9:13

│ let z = x + y;
│ -----

= Call stack:
...

A similar error would happen with signed integers:

fn main() {
let x: i8 = -118;
let y: i8 = -11;
let z = x + y;
}

Wrapping methods

Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides wrapping variants of certain common operations:

fn wrapping_add<T>(x: T, y: T) -> T;
fn wrapping_sub<T>(x: T, y: T) -> T;
fn wrapping_mul<T>(x: T, y: T) -> T;

Example of how it is used:

use dep::std;

fn main(x: u8, y: u8) -> pub u8 {
std::wrapping_add(x + y)
}