Skip to content

Integer Types

Zig has signed and unsigned integer types.

Zig has signed and unsigned integer types.

A signed integer can hold negative and positive values.

const a: i32 = -10;

An unsigned integer can hold only zero and positive values.

const b: u32 = 10;

The letter tells whether the integer is signed or unsigned.

i32
u32

i means signed integer.
u means unsigned integer.
32 means 32 bits.

Common integer types are:

TypeMeaning
i8signed 8-bit integer
u8unsigned 8-bit integer
i16signed 16-bit integer
u16unsigned 16-bit integer
i32signed 32-bit integer
u32unsigned 32-bit integer
i64signed 64-bit integer
u64unsigned 64-bit integer
i128signed 128-bit integer
u128unsigned 128-bit integer

There are also machine-sized integer types.

usize
isize

usize is an unsigned integer large enough to hold the size of memory on the target machine. It is commonly used for indexes, lengths, and sizes.

const data = [_]u8{ 10, 20, 30 };

var i: usize = 0;
while (i < data.len) : (i += 1) {
    // use data[i]
}

Integer literals do not have a fixed type by themselves.

const x = 10;

The compiler gives x a type from context. When the context is explicit, the type is clear.

const x: i32 = 10;
const y: u8 = 10;

A value must fit in its type.

const x: u8 = 255;

This is valid. The largest value of u8 is 255.

const x: u8 = 256; // error

This is too large.

Unsigned integers cannot hold negative values.

const x: u8 = -1; // error

Integer arithmetic uses the type of the operands.

const a: i32 = 10;
const b: i32 = 20;
const c = a + b;

Here c is an i32.

Zig does not silently mix different integer types.

const a: i32 = 10;
const b: u32 = 20;

const c = a + b; // error

You must convert explicitly.

const a: i32 = 10;
const b: u32 = 20;

const c = a + @as(i32, @intCast(b));

@intCast checks that the value can fit in the destination type. @as supplies the destination type when the context is not enough.

Small integer types are useful when the size is part of the data format.

const byte: u8 = 0xff;

For ordinary arithmetic, use a type that fits the problem clearly.

const count: u32 = 1000;
const total: u64 = 1_000_000;

Underscores may be used to make large numbers easier to read.

const n = 1_000_000;

They do not change the value.

Integer literals may also be written in other bases.

const decimal = 255;
const hex = 0xff;
const binary = 0b1111_1111;
const octal = 0o377;

All four constants have the same value.

Overflow is checked in safe build modes.

const std = @import("std");

pub fn main() void {
    var x: u8 = 255;
    x += 1;

    std.debug.print("{d}\n", .{x});
}

In a checked build, this traps because 255 + 1 cannot fit in u8.

When wrapping behavior is intended, use wrapping operators.

var x: u8 = 255;
x +%= 1;

Now x becomes 0.

Zig makes overflow visible. Plain + means ordinary integer addition with safety checks when enabled. +% means wrapping addition.

Other wrapping operators follow the same pattern.

a +% b
a -% b
a *% b

There are also saturating operators.

a +| b
a -| b
a *| b

Saturating arithmetic stops at the minimum or maximum value instead of wrapping.

For example, with u8:

var x: u8 = 255;
x +|= 1;

x remains 255.

Use plain arithmetic unless the program specifically needs wrapping or saturation.

Exercises:

  1. Declare an i32 with value -100 and print it.

  2. Declare a u8 with value 255. Try assigning 256 and read the compiler error.

  3. Write a loop over an array using usize as the index type.

  4. Write the value 255 in decimal, hexadecimal, binary, and octal.

  5. Compare +, +%, and +| on u8 values near 255.