Primitives¶
Primitive types hold simple values: whole numbers, decimal numbers, booleans, and characters. Their methods are available without imports. See Types for sizes and literal forms.
At a glance
- Integers, floating-point numbers,
bool, andcharare stored inline. - Their methods are available without imports.
Package: system.primitives (available without an import)
Primitive types are the built-in scalars: integers, floats, booleans, and characters. Their methods ship in the built-in library, so you can call them anywhere without an import. For the full type list and literal suffixes, see Types.
Primitive types¶
| Type | Size | Range | Literal |
|---|---|---|---|
int |
32-bit signed | −2,147,483,648 to 2,147,483,647 | 42 (default for integer literals) |
uint |
32-bit unsigned | 0 to 4,294,967,295 | 42u |
long |
64-bit signed | −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 42L |
ulong |
64-bit unsigned | 0 to 18,446,744,073,709,551,615 | 42uL |
isize |
Pointer width, signed | Same as int on wasm32, same as long on 64-bit native |
No suffix; (isize)42 |
usize |
Pointer width, unsigned | Same as uint on wasm32, same as ulong on 64-bit native |
No suffix; (usize)42 |
byte |
8-bit unsigned | 0 to 255 | 255b |
float16 |
16-bit IEEE 754 (binary16) | ±65,504, ~3 significant digits | 1.5h |
float |
32-bit IEEE 754 | About ±3.4 × 10³⁸, ~7 significant digits | 3.14f, or bare 3.14 |
double |
64-bit IEEE 754 | About ±1.8 × 10³⁰⁸, ~15 significant digits | 3.14d |
bool |
— | true or false |
true |
char |
32 bits | One Unicode scalar value | 'A', '\n' |
Suffixes are case-insensitive. Integer literals can also be written in hex (0xFF), octal (0o77), or binary (0b101); a hex literal accepts only the u, L, and uL suffixes, because b, d, and f are hex digits.
Methods by type¶
| Method | Available on | Returns | Effect |
|---|---|---|---|
.compare(other) |
Every numeric type, char |
int |
Negative, zero, or positive as this value is less than, equal to, or greater than other |
.min(other) / .max(other) |
Every numeric type | Same type | The smaller / larger of the two values |
.clamp(lo, hi) |
Every integer type | Same type | This value constrained to the inclusive range [lo, hi] |
.abs() |
int, long, float16, float, double |
Same type | Absolute value |
.is_nan() / .is_infinite() |
float16 |
bool |
Whether the value is NaN / positive or negative infinity |
.signum() |
int, long |
Same type | -1, 0, or 1 by sign |
.pow(exp) |
int |
int |
This value raised to exp; an exponent of 0 or less gives 1 |
.to_int() |
bool |
int |
1 for true and 0 for false |
.is_digit() / .is_alpha() / .is_whitespace() |
char |
bool |
ASCII classification |
.to_lower() / .to_upper() |
char |
char |
ASCII case conversion; other characters are unchanged |
.as_string() |
char |
string |
A new one-character string |
Type.parse(text) |
Every primitive type | Result<Type, ParseError> |
Static; parses decimal text, reporting bad or out-of-range input as Err |
Because every numeric type and char implements compare, they all satisfy Comparable, so a List of them can be sorted directly.
Integers¶
Integers come in signed and unsigned forms in several widths (see the table above). Common methods:
.min(other)/.max(other)— the smaller / larger of two values..clamp(lo, hi)— constrain to the inclusive range[lo, hi]..abs()and.signum()— available onintandlong.Type.parse(str)— static; parses a string into that integer type, returningResult<Type, ParseError>.
System.println(15.clamp(0, 10)); // 10
System.println((-5).abs()); // 5
let n = int.parse("42").unwrap_or(0); // 42
Integer overflow¶
Integer arithmetic wraps by default: an operation whose mathematical result does not fit its type wraps modulo the type's bit width (two's complement), in debug and release builds alike. Nothing is silently promoted to a wider type.
- A binary op's result type is its left operand's type —
byte + bytestaysbyte, so250b + 10bis4b. +,-,*, and unary-wrap:2147483647 + 1is-2147483648, anduint0u - 1uis4294967295u./and%panic on a zero divisor.-2147483648 / -1(the minimum signed value divided by-1) wraps to-2147483648, and the matching%is0.- Shift counts are masked to the type's bit width:
1 << 33on anintis1 << 1. Bits shifted out of the value are discarded:1 << 31is-2147483648.
Wrap a checked { } block around code where an overflow is a bug you want to catch (sizes, offsets, money). Inside it, every integer op lexically in the block panics instead of wrapping:
| Operation | Default (wrapping) | Inside checked { } |
|---|---|---|
+, -, *, unary - |
Wraps around the type's bit width | Panics when the result is outside the type |
/, % by zero |
Panics | Panics |
/, % of the minimum signed value by -1 |
Wraps (/) or gives 0 (%) |
Panics |
<<, >> |
Shift count is masked to the bit width | Panics when the count is negative or at least the bit width |
unchecked { } restores wrapping inside a checked region:
let count = 100000;
let size = 100000;
let h = 2166136261u;
checked {
let total = count * size; // panic: attempt to multiply with overflow
unchecked {
h = (h ^ 97u) * 16777619u; // FNV-1a step: wraps at 32 bits
}
}
The mode is lexical: it applies to the statements written inside the block (including lambdas written there), not to functions they call. checked and unchecked are only keywords directly before {, so they stay usable as identifiers.
Inside checked blocks, the optimizer removes checks it can prove never fire — a loop counter bounded by i < n, a masked x & 255, an array length.
Type.parse(str) reports out-of-range text as Err(ParseError) rather than panicking.
Floating point¶
IEEE 754, in three widths:
float16— 16-bit half precision (1.5h). Compact storage for large arrays of approximate values such as machine-learning weights or color channels.float— 32-bit (3.14f). An unsuffixed3.14is alsofloatunless the expected type isdouble.double— 64-bit. Use3.14dfor an always-double literal, or a bare3.14/0when the expected type isdouble.
Common methods:
.abs()— absolute value..min(other)/.max(other).double.parse(str)— static; parses a string into adouble, returningResult<double, ParseError>.
System.println((3.14f).abs()); // 3.14
System.println((1.5f).min(2.0f)); // 1.5
let d = double.parse("2.5").unwrap_or(0.0d);
Printing floating-point values¶
System.print, to_string, string concatenation, StringBuilder.append_double, and collection or struct formatting all write the same text for a floating-point value: the shortest decimal that rounds back to exactly that value in its own width. The output is identical on every target.
System.println(0.1d); // 0.1
System.println(0.1d + 0.2d); // 0.30000000000000004
System.println(1.0f / 3.0f); // 0.33333334 (float keeps about 7 digits)
System.println(3.0d); // 3
Magnitudes from 1e-7 up to, but not including, 1e21 print in plain notation, such as 0.000001 or 1500000. Smaller and larger magnitudes print with an exponent, such as 1e-7, 1.5e-8, or 1e+21. NaN prints as nan, the infinities as inf and -inf, and both zeros as 0.
Half precision: float16¶
A float16 keeps 11 significant bits (about 3 decimal digits) and holds finite values up to ±65,504. Values smaller than 6.1 × 10⁻⁵ are stored as subnormals, down to about 6 × 10⁻⁸.
let a: float16 = 1.5h;
let b = (float16)0.25;
let c = a * b + 2.0h; // 2.375
let wide: float = c; // float16 widens to float and double implicitly
System.println(c); // 2.375
System.println((float16)0.1); // 0.1 (stored as 0.0999755859375)
System.println(float16.max_value); // 65500
Conversions follow these rules:
float16widens implicitly tofloatanddouble.- Converting a
floatordoubletofloat16needs a cast. The cast rounds to the nearestfloat16, choosing the even neighbor on a tie, and values beyond ±65,504 become infinity:(float16)2049.0is2048, and(float16)70000.0is infinity. - Converting between integers and
float16needs a cast in both directions. Casting to an integer drops the fraction, and NaN becomes0. - A
float16literal outside the finite range, such as70000.0h, is a compile error.
Arithmetic and comparisons work like the other floating-point types. Each result is rounded to the nearest float16, so 2048.0h + 1.0h is 2048. Because the result of a binary operator has its left operand's type, put the wider operand first when mixing widths: 1.0f + a is a float, while a + 1.0f is a compile error.
Printing a float16 (or converting it with to_string) produces the shortest decimal text that reads back as the same value, so float16.parse(x.to_string()) always returns x. Infinity and NaN print as inf, -inf, and nan, and float16.parse accepts those words too.
| Member | Value |
|---|---|
float16.max_value |
65504, the largest finite value |
float16.min_value |
-65504, the most negative finite value |
float16.epsilon |
0.0009765625 (2⁻¹⁰), the gap between 1 and the next float16 |
x.is_nan() |
true when x is NaN, for example 0.0h / 0.0h |
x.is_infinite() |
true when x is positive or negative infinity |
let parsed = float16.parse("0.125").unwrap_or(0.0h); // 0.125
let zero = 0.0h;
let nan = zero / zero;
System.println(nan.is_nan()); // true
System.println(((float16)1.0e10).is_infinite()); // true
@json classes encode float16 fields as JSON numbers using the same shortest text, and decoding rounds the number to the nearest float16. extern and @export functions cannot take or return float16, because host and C interfaces have no portable 16-bit float type; widen to float at those boundaries.
Booleans¶
bool is true or false.
.to_int()—1fortrue,0forfalse.bool.parse(str)— static; accepts exactly"true"or"false"(case-sensitive), returningResult<Unit, ParseError>.
System.println(true.to_int()); // 1
let b = bool.parse("true").unwrap_or(false); // true
let bad = bool.parse("True").unwrap_or(false); // false — not exact "true"
Characters¶
char is a single character (one Unicode scalar value). Write literals in single quotes: 'A', '\n', 'é'.
.is_digit()/.is_alpha()/.is_whitespace()— classify the character (ASCII rules)..to_lower()/.to_upper()— ASCII case conversion.(int)c— the numeric code point of characterc..as_string()— a new single-character string.char.parse(str)— static; requires exactly one Unicode scalar instr; returnsResult<char, ParseError>.