Native integer operations

Mypyc supports these integer types:

  • int (arbitrary-precision integer)

  • i64 (64-bit signed integer)

  • i32 (32-bit signed integer)

  • i16 (16-bit signed integer)

  • u8 (8-bit unsigned integer)

i64, i32, i16 and u8 are native integer types and are available in the mypy_extensions module. int corresponds to the Python int type, but uses a more efficient runtime representation (tagged pointer). Native integer types are value types.

All integer types have optimized primitive operations, but the native integer types are more efficient than int, since they don’t require range or bounds checks.

Operations on integers that are listed here have fast, optimized implementations. Other integer operations use generic implementations that are generally slower. Some operations involving integers and other types, such as list indexing, are documented elsewhere.

Construction

int type:

  • Integer literal

  • int(x: float)

  • int(x: i64)

  • int(x: i32)

  • int(x: i16)

  • int(x: u8)

  • int(x: str)

  • int(x: str, base: int)

  • int(x: int) (no-op)

i64 type:

  • i64(x: int)

  • i64(x: float)

  • i64(x: i64) (no-op)

  • i64(x: i32)

  • i64(x: i16)

  • i64(x: u8)

  • i64(x: str)

  • i64(x: str, base: int)

i32 type:

  • i32(x: int)

  • i32(x: float)

  • i32(x: i64) (truncate)

  • i32(x: i32) (no-op)

  • i32(x: i16)

  • i32(x: u8)

  • i32(x: str)

  • i32(x: str, base: int)

i16 type:

  • i16(x: int)

  • i16(x: float)

  • i16(x: i64) (truncate)

  • i16(x: i32) (truncate)

  • i16(x: i16) (no-op)

  • i16(x: u8)

  • i16(x: str)

  • i16(x: str, base: int)

Conversions from int to a native integer type raise OverflowError if the value is too large or small. Conversions from a wider native integer type to a narrower one truncate the value and never fail. More generally, operations between native integer types don’t check for overflow.

Implicit conversions

int values can be implicitly converted to a native integer type, for convenience. This means that these are equivalent:

from mypy_extensions import i64

def implicit() -> None:
    # Implicit conversion of 0 (int) to i64
    x: i64 = 0

def explicit() -> None:
    # Explicit conversion of 0 (int) to i64
    x = i64(0)

Similarly, a native integer value can be implicitly converted to an arbitrary-precision integer. These two functions are equivalent:

def implicit(x: i64) -> int:
    # Implicit conversion from i64 to int
    return x

def explicit(x: i64) -> int:
    # Explicit conversion from i64 to int
    return int(x)

Operators

  • Arithmetic (+, -, *, //, /, %)

  • Bitwise operations (&, |, ^, <<, >>, ~)

  • Comparisons (==, !=, <, etc.)

  • Augmented assignment (x += y, etc.)

If one of the above native integer operations overflows or underflows with signed operands, the behavior is undefined. Signed native integer types should only be used if all possible values are small enough for the type. For this reason, the arbitrary-precision int type is recommended for signed values unless the performance of integer operations is critical.

Operations on unsigned integers (u8) wrap around on overflow.

It’s a compile-time error to mix different native integer types in a binary operation such as addition. An explicit conversion is required:

from mypy_extensions import i64, i32

def add(x: i64, y: i32) -> None:
    a = x + y  # Error (i64 + i32)
    b = x + i64(y)  # OK

You can freely mix a native integer value and an arbitrary-precision int value in an operation. The native integer type is “sticky” and the int operand is coerced to the native integer type:

def example(x: i64, y: int) -> None:
    a = x * y
    # Type of "a" is "i64"
    ...
    b = 1 - x
    # Similarly, type of "b" is "i64"

Statements

For loop over a range is compiled efficiently, if the range(...) object is constructed in the for statement (after in):

  • for x in range(end)

  • for x in range(start, end)

  • for x in range(start, end, step)

If one of the arguments to range in a for loop is a native integer type, the type of the loop variable is inferred to have this native integer type, instead of int:

for x in range(i64(n)):
    # Type of "x" is "i64"
    ...