Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(docs): Document signed integers and integer overflow behavior #3393

Merged
merged 19 commits into from
Nov 2, 2023
Merged
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 90 additions & 23 deletions docs/docs/language_concepts/data_types/01_integers.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,104 @@
---
title: Integers
description:
Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code.
keywords:
[
noir,
integer types,
methods,
examples,
arithmetic,
]
description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code.
keywords: [noir, integer types, methods, examples, arithmetic]
---

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

> **Note:** 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 `Field`s are not possible.

An integer type is specified first with the letter `u`, indicating its unsigned nature, followed by
its length in bits (e.g. `32`). For example, a `u32` variable can store a value in the range of
$\\([0,2^{32}-1]\\)$.
## Unsigned Integers

> **Note:** The default proving backend supports both even (e.g. `u16`, `u48`) and odd (e.g. `u5`, `u3`)
> sized integer types.
An unsigned integer type is specified first with the letter `u`, indicating its unsigned nature, followed by its length in bits (e.g. `8`):

Taking a look of how the type is used:
```rust
fn main() {
let x : u8 = 1;
let y : u8 = 1;
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let z = x + y;
assert (z == 2);
}
```

The length in bits determines the boundaries the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$).

## Signed Integers

A signed integer type is specified first with the letter `i`, stands for integer, followed by its length in bits (e.g. `8`):
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

```rust
fn main(x : Field, y : u32) {
let z = x as u32 + y;
fn main() {
let x : i8 = -1;
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved
let y : i8 = -1;
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved
let z = x + y;
assert (z == -2);
}
```

`x`, `y` and `z` are all private values in this example. However, `x` is a field while `y` and `z`
are unsigned 32-bit integers. If `y` or `z` exceeds the range $\\([0,2^{32}-1]\\)$, proofs created
will be rejected by the verifier.
The length in bits determines the boundaries the integer type can store. For example, a `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$).
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

## Overflows

Computations that exceed the type boundaries would result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove:
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

```rust
fn main(x : u8, y : u8) {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let z = x + y;
}
```

With:

```toml
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 unsigned integers, for example while trying to prove:
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

```rust
fn main() {
guipublic marked this conversation as resolved.
Show resolved Hide resolved
let x : i8 = -118;
let y : i8 = -11;
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved
let z = x + y;
}
```

> **Note:** The default proving backend supports both even (e.g. _u2_) and odd (e.g. _u3_) arbitrarily-sized integer types up to _u127_.

### Wrapping methods

Although integer overflow is expected to error, it is understood that some use-cases may actually want to rely on wrapping.
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

For that, you can import and use these `wrapping` functions from the standard library, which are defined as:
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved

```rust
wrapping_add<T>(x : T, y: T) -> T
wrapping_sub<T>(x : T, y: T) -> T
wrapping_mul<T>(x : T, y: T) -> T
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved
```

example usage:

```rust
use dep::std;

fn main(x : u8, y : u8) {
let z = std::wrapping_add(x + y);
}
Savio-Sou marked this conversation as resolved.
Show resolved Hide resolved
```
Loading