Turmeric provides fixed-width numeric types alongside the canonical int
(64-bit signed) and float (64-bit IEEE 754) types. This guide explains
the available types, their literal syntax, the coercion rule, and how to
cast between them.
| Type name | Short alias | C type | Width | Signed |
|---|---|---|---|---|
int8 |
i8 |
int8_t |
8 bit | yes |
int16 |
i16 |
int16_t |
16 bit | yes |
int32 |
i32 |
int32_t |
32 bit | yes |
int64 |
i64 |
int64_t |
64 bit | yes |
uint8 |
u8 |
uint8_t |
8 bit | no |
uint16 |
u16 |
uint16_t |
16 bit | no |
uint32 |
u32 |
uint32_t |
32 bit | no |
uint64 |
u64 |
uint64_t |
64 bit | no |
float32 |
f32 |
float |
32 bit | -- |
float64 |
f64 |
double |
64 bit | -- |
int is an alias for int64 and float is an alias for float64. All
other types are distinct numeric kinds at both the type-system and C-codegen
levels.
Append a suffix to an integer or float literal to fix its kind:
42i8 ; int8 -- value 42
200u8 ; uint8 -- value 200
1000i16 ; int16
0xFFu32 ; uint32 -- hex literal
2.5f32 ; float32
3.14159f64 ; float64 (same as 3.14159)
Unsuffixed integer literals have type int (int64). Unsuffixed float
literals have type float (float64).
(as ...) ExplicitlyTurmeric does not implicitly widen or narrow numeric values. Mixing two distinct numeric kinds in an arithmetic or comparison expression is a compile error (TUR-E0042):
(+ 1i8 2i16) ; error TUR-E0042: int8 and int16 cannot be mixed
(+ 1i32 2) ; error TUR-E0042: int32 and int (int64) cannot be mixed
(< 1.5f32 2.0) ; error TUR-E0042: float32 and float cannot be mixed
To operate on values of different widths, cast one side with (as type expr):
(+ 1i8 (as int8 2i16)) ; narrow 2i16 to int8
(+ (as int16 1i8) 2i16) ; widen 1i8 to int16
(+ 1i32 (as int32 2)) ; narrow canonical int to int32
(+ (as int 1i32) 2) ; widen int32 to int64
(< 1.5f32 (as float32 2.0)) ; narrow float64 literal to float32
Implicit numeric coercion makes it easy to accidentally widen a
computation to 64 bits where a 32-bit or narrower representation was
intended. It also obscures the ABI boundary: a function declared to return
:int32 should not silently widen every intermediate value to int64_t in
the emitted C. Requiring explicit (as ...) makes the intent visible in
source and keeps the generated C free of unintended int64_t round-trips.
All standard arithmetic and comparison operators are available for each numeric kind. Both operands must have the same kind:
(+ 10i8 20i8) ; int8 + int8 -> int8
(mod 100u32 7u32) ; uint32 mod uint32 -> uint32
(< 1i32 2i32) ; int32 < int32 -> bool
(= 42u8 42u8) ; uint8 = uint8 -> bool
(+ 1.5f32 2.5f32) ; float32 + float32 -> float32
(as ...)(as TargetType expr) converts a value to a different numeric kind. The
conversion is the same as a C explicit cast -- no overflow checking is
performed:
(as int8 300) ; wraps: 300 mod 256 = 44
(as uint8 -1) ; wraps: 255
(as float32 1i32) ; integer -> float
(as int32 3.7f32) ; truncates: 3
Casts between signed and unsigned types of the same width reinterpret the bit pattern at the C level.
Struct fields can use any numeric kind:
(defstruct Pixel
[r :uint8
g :uint8
b :uint8
a :uint8])
(defstruct Vec3f
[x :float32
y :float32
z :float32])
The generated C struct uses the corresponding <stdint.h> types
(uint8_t, float, etc.) directly, with no int64_t boxing for fields
of fully-known width.
bit-and, bit-or, bit-xor, bit-shl, and bit-shr operate on the
canonical int type. To use them on narrower types, cast to int, operate,
then cast back:
(let [x 0xF0u8
y 0x0Fu8]
(as uint8 (bit-and (as int x) (as int y)))) ; => 0u8
See Phase C of the unboxing plan for kind-preserving bitwise ops.
The numeric-types-cast fixture covers the main casting scenarios. The
sized-mixed-arith-error fixture covers every operator that rejects
mixed-width operands and expects TUR-E0042 on each.