Types
Sized integers: byte, int8, int16, int32, uint
The fixed-width integer types, how arithmetic wraps, how mixed widths combine, and when conversions throw.
since 0.1.0-alpha.1linuxwindowswasm
Description
Besides the 64-bit int, the language has five fixed-width integer types:
| type | width | range |
|---|---|---|
byte |
8 bits, unsigned | 0 to 255 |
int8 |
8 bits, signed | -128 to 127 |
int16 |
16 bits, signed | -32768 to 32767 |
int32 |
32 bits, signed | -2147483648 to 2147483647 |
uint |
32 bits, unsigned | 0 to 4294967295 |
They default to 0 and support the full integer operator set: + - * / %, & | ^ << >>, unary - and ~, the compound assignments and the comparisons. There is no uint8, uint16 or uint64; byte is the unsigned 8-bit type.
Arithmetic wraps silently at the type's own width. A conversion that cannot represent its value throws instead.
Wrapping arithmetic
byte b = 255;
b = b + 1;
console.writeln(b);
byte z = 0;
z = z - 1;
console.writeln(z);
int8 s = 127;
s = s + 1;
console.writeln(s);
int8 neg = -8;
console.writeln(neg >> 1);
uint u = 0;
u = u - 1;
console.writeln(u);
uint big = 3000000000;
console.writeln(big / 2);
console.writeln(big > 5);
0
255
-128
-4
4294967295
1500000000
true
Rules
Wrapping.
- Arithmetic wraps at the working width. For a signed type the result is two's complement:
127 + 1asint8is-128.uintarithmetic wraps modulo 2 to the 32nd power:0 - 1is4294967295. ~reduces to the type's width: for abytezero it gives255, for auintzero it gives4294967295.>>on a signed type is an arithmetic shift, soint8-8 >> 1is-4. Onbyteanduintit is a logical shift. Auintabove 2 to the 31st power keeps its true value:3000000000 / 2is1500000000.- Division or remainder by zero throws a catchable
RuntimeException. - A shift count must be inside the working width:
0..7for an 8-bit result,0..15,0..31and0..63for 16, 32 and 64 bits. Outside that range the shift throws. A shift by anintvariable computes inint, so its bound is0..63. - A minimum value divided by
-1wraps at every sized width.
Mixed widths. Two operands of different types are combined at a common working type:
- Two signed integers, or two unsigned ones, give the wider of the two:
byte + uintisuint,int8 + int16isint16,byte + intisint. - A signed and an unsigned integer give the smallest signed type whose range covers both:
int8 + byteisint16,int32 + byteisint32.uintwith any signed type, includingint32, givesint, because coveringuintneeds 33 signed bits. - A bare literal beside a typed operand takes that operand's type, so
b * 2with abytebcomputes at 8 bits. A variable of typeintdoes not:b << countwithint countcomputes inint. - The result of every operator is reduced to the working type, so wrapping happens at the joined width, not at the width of the left operand.
Literals and conversions.
- A literal stored in a sized type is range-checked at compile time.
byte b = 200;is fine;byte b = 256;andbyte b = -1;are compile errors that name the range. - Storing an integer value into a narrower type is a conversion and throws when the value does not fit.
int8 c = b;throws for abytebof200. int.toByte(),toInt8(),toInt16(),toInt32()andtoUint()convert fromintand throw when the value is out of range.toInt()on a sized integer always succeeds. To narrow without throwing, mask first:(n & 255).toByte().byteanduinthave noabs()orsign(). Calling a method that does not exist on a primitive is not a compile error; it throws aRuntimeExceptionreading "cannot resolve call target" when the program runs.int8,int16andint32haveabs()andsign();abs()of the minimum value wraps to itself.
Distinct types. Each width is its own type at run time. is and match tell a byte from an int holding the same number, and values of different widths are different keys in a Map.
Examples
Joined widths and shift limits:
Mixed widths and shifts
byte b = 200;
int8 i8 = -5;
console.writeln(b + 100);
console.writeln(i8 + b);
console.writeln((i8 + b) is int16);
console.writeln((b + 1) is byte);
int count = 8;
console.writeln(b << count);
try {
console.writeln(b << 8);
} catch (RuntimeException e) {
console.writeln("caught: ${e.message}");
}
44
195
true
true
51200
caught: shift count out of range
Conversions throw; masking does not:
Conversions that throw
int n = 300;
byte b = 200;
try {
console.writeln(n.toByte());
} catch (RuntimeException e) {
console.writeln("caught: ${e.message}");
}
try {
int8 c = b;
console.writeln(c);
} catch (RuntimeException e) {
console.writeln("caught: ${e.message}");
}
console.writeln((n & 255).toByte());
console.writeln(b.toInt() + 100);
caught: toByte: value out of range (0..255)
caught: value 200 out of range for 'int8' (-128..127)
44
300
A byte and an int with the same value are distinguishable in a union:
Sized integers in a union
byte b = 7;
int n = 7;
int | byte either = b;
match (either) {
byte => console.writeln("a byte");
int => console.writeln("an int");
}
either = n;
match (either) {
byte => console.writeln("a byte");
int => console.writeln("an int");
}
a byte
an int
Notes
The methods of the sized integers (toString, toHex, min, max, clamp, pow, toInt, toFloat and the others) are listed in the library reference. See lang.numeric-conversions for how a value is converted when it is stored into a typed slot.
See also
- Numeric conversions and working types — How operands of different numeric types combine, and when a value is converted on its way into a typed variable, field, parameter or return.
- Sized floats: float8, float16, float32 — The narrow floating-point formats, how every operation re-rounds, and where they overflow or throw.
- int — The signed 64-bit integer type, the default type of whole numbers.