Integers
An integer type holds a whole number in a fixed number of bits. The signed types int8 to int512 use two's complement; the unsigned types uint8 to uint512 hold zero and up. int and uint are as wide as a pointer.
The integer types
| Type | Bits | Bytes | Min | Max | Suffix |
|---|---|---|---|---|---|
int8 | 8 | 1 | −128 | 127 | i8 |
int16 | 16 | 2 | −32,768 | 32,767 | i16 |
int32 | 32 | 4 | −2,147,483,648 | 2,147,483,647 | i32 |
int64 | 64 | 8 | −263 ≈ −9.22 × 1018 | 263 − 1 | i64 |
int128 | 128 | 16 | −2127 ≈ −1.70 × 1038 | 2127 − 1 | i128 |
int256 | 256 | 32 | −2255 ≈ −5.79 × 1076 | 2255 − 1 | i256 |
int512 | 512 | 64 | −2511 ≈ −6.70 × 10153 | 2511 − 1 | i512 |
int | 64 | 8 | as int64 | as int64 | i |
uint8 | 8 | 1 | 0 | 255 | u8 |
uint16 | 16 | 2 | 0 | 65,535 | u16 |
uint32 | 32 | 4 | 0 | 4,294,967,295 | u32 |
uint64 | 64 | 8 | 0 | 264 − 1 ≈ 1.84 × 1019 | u64 |
uint128 | 128 | 16 | 0 | 2128 − 1 ≈ 3.40 × 1038 | u128 |
uint256 | 256 | 32 | 0 | 2256 − 1 ≈ 1.16 × 1077 | u256 |
uint512 | 512 | 64 | 0 | 2512 − 1 ≈ 1.34 × 10154 | u512 |
uint | 64 | 8 | 0 | as uint64 | u |
byte is a built-in alias of uint8, for values that are raw storage rather than numbers.
int and uint are pointer-sized: their width follows the target, and it is 64 bits on every target rux 0.4.0 supports. int is the type of an unsuffixed integer literal and of Main's exit status; uint is the type of lengths, indices and sizeof. When a value must keep a width across targets — a file format, a protocol, a C structure — name a fixed-width type.
The 128-, 256- and 512-bit types are ordinary integers: they support every operator and conversion below.
Associated constants
Every integer type has four constants. They are declared in the standard Core package, so the type has to be imported from it, and Core listed under [Dependencies]:
| Constant | Type | Value |
|---|---|---|
Bits | uint | Width in bits |
Bytes | uint | Storage size in bytes |
Min | the type | Smallest value |
Max | the type | Largest value |
import Core::{ int, int8, uint64 };
import Io::PrintLine;
func Main() -> int {
PrintLine("int8 {} to {}", int8::Min, int8::Max); // -128 to 127
PrintLine("uint64 max {}", uint64::Max); // 18446744073709551615
PrintLine("int {} bits", int::Bits); // 64
return 0;
}
Without the import, the constant is not found: error: 'Max' not found in extend for type 'int8'. An alias carries the constants of its target, so byte::Max is 255 once byte is imported.
Literals
Integer literals are decimal, hexadecimal (0x), octal (0o) or binary (0b), with _ between digits, and an optional suffix from the table above. An unsuffixed literal takes the type its context requires — the declared type it initialises, or the other operand's type — and is an int otherwise. It must fit that type:
let level: uint8 = 200;
let raised = level + 100; // 100 is a uint8
let mask = 0xFF00u16;
let tooBig: uint8 = 256; is error: integer literal is out of range for type 'uint8'. Literals gives the full spelling rules.
Operations
| Operators | Result | Rules |
|---|---|---|
+ - * | The operand type | Wrap around modulo 2bits |
/ % | The operand type | Truncate toward zero; panic on a zero divisor |
prefix - | The operand type | Wraps; on an unsigned type, gives 2bits − x |
& | ^ ~ | The operand type | Bitwise |
<< >> >>> | The left operand's type | See Shift |
== != < <= > >= | bool | Numeric order |
++ -- and compound assignment | — | As the operator they stand for |
Wrapping
+, - and * wrap: a result that does not fit keeps its low bits, in every build profile.
var level: uint8 = 250;
level += 10; // 4
var small: int8 = 127;
small += 1; // -128
When overflow must be detected, use the Core functions AddChecked, SubChecked and MulChecked, which report it, or AddSaturating and its siblings, which stop at the limits:
import Core::{ AddChecked, AddSaturating };
import Io::PrintLine;
func Main() -> int {
let level: uint8 = 250;
var sum: uint8 = 0;
if AddChecked(level, 10u8, @sum) {
PrintLine("overflow; wrapped to {}", sum); // 4
}
PrintLine("saturated: {}", AddSaturating(level, 10u8)); // 255
return 0;
}
Division
/ truncates toward zero and % takes the sign of the dividend: -7 / 2 is -3, and -7 % 2 is -1.
A zero divisor stops the program with a panic, on every target and in every build profile. So does the one signed quotient that does not fit, a type's Min divided by -1, and the matching %:
Panic: division by zero
at Divide (Src/Main.rux:5:14)
Panic: division overflow
at Main (Src/Main.rux:16:23)
Mixed operands
Two operands of the same signedness meet at the wider type: an int32 plus an int64 is an int64. Operands of different signedness are an error, since either conversion could change a value — convert one with as to choose:
error: operator '+' cannot combine left operand 'uint64' with right operand 'int64'
error: operator '<' cannot compare left operand 'uint64' with right operand 'int64'
Conversions
An integer widens implicitly to a wider integer of the same signedness, and int and int64 (likewise uint and uint64) convert to each other freely. Everything else is written with as:
| Conversion | Result |
|---|---|
| To a narrower integer | Keeps the low bits: 300 as int8 is 44 |
| Between signed and unsigned | Keeps the bit pattern: -1 as uint8 is 255 |
| To a wider integer | Sign-extends a signed value: (-1i32) as uint64 is 264 − 1 |
| To a floating-point type | The nearest representable value |
| To a boolean type | false for zero, true for anything else |
| To a character type | The character with that code; see Characters |
let big: int32 = 300;
let narrowed = big as int8; // 44
let negative: int32 = -1;
let unsigned = negative as uint8; // 255
let ratio = 7 as float64 / 2.0; // 3.5
let flag = 256 as bool; // true
A narrowing as never fails; it produces a different value. To find out whether a value fits first, compare it with the target's Min and Max, or call Core::ConvertChecked, which returns true when the conversion lost the value.
Implicit widening refuses what it cannot do losslessly: let d: int16 = c; for a uint8 c is error: cannot assign 'uint8' to 'int16', and let i: int32 = n; for an int n is error: cannot assign 'int' to 'int32'.
See also
- Floating-Point — the other numeric family
- Arithmetic, Bitwise, Shift — the operators in full
- Casts —
asfor every type - Integer, Number limit, Wide integer — lessons
- Wrapping arithmetic, Checked arithmetic, Checked convert — lessons on overflow