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 13 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
110 changes: 87 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,101 @@
---
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;
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` (which stands for integer) followed by its length in bits (e.g. `8`):

```rust
fn main(x : Field, y : u32) {
let z = x as u32 + y;
fn main() {
let x: i8 = -1;
let y: i8 = -1;
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 bit count of the integer determines the maximum and minimum integers the type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$).

## 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:

```rust
fn main(x: u8, y: u8) {
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 signed integers, for example while trying to prove:

```rust
fn main() {
guipublic marked this conversation as resolved.
Show resolved Hide resolved
let x: i8 = -118;
let y: i8 = -11;
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, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations:

````rust
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 usage:

```rust
use dep::std;

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