Integers
Integers are whole numbers such as 0, 42, and -7.
Acton has three groups of integer types:
intfor the normal 64-bit signed integer typebigintfor integers that must grow beyond theintrange- explicitly sized signed and unsigned integers such as
i32andu16
bigint lets values grow arbitrarily large ensuring correct program behavior
when you are uncertain about the exact size needed. However, as bigint is
significantly slower than the bounded integer types, do not default to bigint
out of convenience. For the vast majority of normal use cases, int is large
enough and considerably faster. Use exact-width integers when you specifically
need their bit width.
Bounded integer types can often be compiled in an unboxed form, which avoids
boxing overhead and can make arithmetic much faster, several orders of
magnitude, than bigint in tight code. That is another reason to
prefer int or an exact-width integer when the bounded range is the
right fit, and reserve bigint for values that truly need arbitrary
precision.
If you are not sure which integer type to use, start with
int. Move to bigint when values may get very
large, and use the exact-width types when you need to match a protocol,
file format, or external API.
| Type | Min | Max |
|---|---|---|
i8 |
-128 | 127 |
i16 |
-32768 | 32767 |
i32 |
-2147483648 | 2147483647 |
u1 |
0 | 1 |
u8 |
0 | 255 |
u16 |
0 | 65535 |
u32 |
0 | 4294967295 |
u64 |
0 | 18446744073709551615 |
int |
-9223372036854775808 | 9223372036854775807 |
bigint |
arbitrary | arbitrary |
Basic use
actor main(env):
count = 42
port = u16(5000)
huge = bigint(123456789012345678901234567890)
print("count:", count)
print("port:", port)
print("huge:", huge)
print("widened:", int(port))
env.exit(0)
Use int for everyday counting and arithmetic. Use bigint when a
value may exceed the normal machine-sized range. Use exact-width types
when the bit pattern matters.
Integer division and remainder
a // b computes an integer quotient and a % b computes the remainder.
The rounding rule depends on the integer type:
int,i32,i16, andi8round the quotient toward zero. A nonzero remainder has the same sign as the dividend (a).bigintrounds the quotient toward negative infinity (floor division). A nonzero remainder has the same sign as the divisor (b).u1,u8,u16,u32, andu64round the quotient toward zero, which is also rounding down for non-negative operands. For a nonzero divisor, the remainder satisfies0 <= a % b < b. For example,u32(8) // u32(3)andu32(8) % u32(3)both give2.
For example:
a | b | int: a // b | int: a % b | bigint: a // b | bigint: a % b |
|---|---|---|---|---|---|
8 | 3 | 2 | 2 | 2 | 2 |
-8 | 3 | -2 | -2 | -3 | 1 |
8 | -3 | -2 | 2 | -3 | -1 |
-8 | -3 | 2 | -2 | 2 | -2 |
For a nonzero divisor and a quotient that fits the type, both conventions
satisfy a == (a // b) * b + (a % b). The magnitude of the remainder is
less than the magnitude of the divisor. divmod(a, b) returns the same
quotient and remainder as the tuple (a // b, a % b).
Converting operands to bigint can therefore change the quotient and
remainder even when the operand values fit in int.
Why the rounding rules differ
Bounded integers use truncation to match efficient hardware integer
division. For signed types, when the quotient q = a // b fits,
truncation also keeps the product q * b in range. With i8 operands
-128 and 3, truncation gives q = -42 and q * b = -126.
Rounding down would give q = -43 and q * b = -129, which is outside
the i8 range.
bigint has no fixed-width overflow limit. Its floor division gives
useful properties for modular arithmetic: for a positive divisor b,
the remainder is always in 0 through b - 1, even when the dividend
is negative. Numbers that differ by a multiple of b have the same
remainder. For example, with bigint operands, -8, 1, and 4 all
leave remainder 1 when divided by 3. This agrees with Euclidean
division for positive divisors; for negative divisors, bigint can
have a negative remainder.
Converting integers
Convert by calling the target type as a constructor.
int(42)
bigint(42)
u16(255)
Widening to a larger type is straightforward:
small = u16(255)
widened = int(small)
Converting to a narrower type checks that the value fits:
safe = u16(12345)
# u16(70000) would raise ValueError
Large integer literals are inferred by size. Values above the normal
int range may infer as u64 or
bigint. When you care about the exact type, annotate it or
use an explicit constructor.