Nitpick Floating Point Type Family
Nitpick natively supports standard IEEE 754 floating point numbers, extended precision floats, and deterministic fixed-point variants.
1. Floating Point
(flt)
flt32(Single precision)flt64(Double precision)flt128(Quad precision)flt256,flt512(Extended software/SIMD precision)
Literals: Suffix with the type name
(e.g., 3.14f32, 2.71828f64).
Usage Example:
flt32:pi = 3.14159f32;
flt64:high_precision_val = -10.5f64;
2. Fixed Point
(fixN)
To guarantee absolute determinism across different architectures (which often have subtly different hardware FPUs), Nitpick provides a family of native fixed-point types.
Type Family
| Type | Total Bits | Integer Bits | Fractional Bits | Q-Format | IR Representation |
|---|---|---|---|---|---|
fix32 |
32 | 16 | 16 | Q16.16 | LLVM i32 |
fix64 |
64 | 32 | 32 | Q32.32 | LLVM i64 |
fix128 |
128 | 64 | 64 | Q64.64 | %struct.fix128 { [2 x i64] } |
fix256 |
256 | 128 | 128 | Q128.128 | %struct.fix256 { [4 x i64] } |
Design Rationale
- fix32 — Lightweight deterministic arithmetic for game physics, audio DSP, and embedded systems. ~4.8 decimal digits of integer range (±32768) with sub-pixel fractional precision.
- fix64 — General-purpose deterministic math. ~9.6 decimal digits of integer range (±~2.1 billion) with 32-bit fractional precision.
- fix128 — High-precision deterministic math for financial calculations and scientific simulations. ~19.2 decimal digits.
- fix256 — Maximum precision for multi-body physics simulations, cryptographic numeric proofs, and large-scale scientific computations. ~38.5 decimal digits.
Literals
Suffix with the type name:
fix32:speed = 42.5fix32;
fix64:position = 1000.123fix64;
fix128:precise = 3.14159265358979fix128;
fix256:velocity = 299792458.0fix256;
Arithmetic
All arithmetic is performed in pure integer math for determinism. Overflow produces the ERR sentinel.
fix64:a = fix64_from_int(10);
fix64:b = fix64_from_int(3);
fix64:sum = a + b; // 13
fix64:prod = a * b; // 30
fix64:quot = a / b; // 3 (with fractional precision)
ERR Sentinel
Each fixed-point type has a dedicated ERR sentinel value (the minimum signed value for that width). ERR propagates stickily: any operation with an ERR operand returns ERR.
| Type | ERR Sentinel |
|---|---|
fix32 |
0x80000000 (INT32_MIN) |
fix64 |
0x8000000000000000 (INT64_MIN) |
fix128 |
{0, 0x8000000000000000} |
fix256 |
{0, 0, 0, 0x8000000000000000} |
Test for ERR with the ok() builtin:
fix64:result = a / fix64_from_int(0); // ERR (division by zero)
if (ok(result) != 0) {
// result is valid
}
Intrinsics
All fixed-point types support the same intrinsic set, prefixed with the type name:
| Category | Intrinsics |
|---|---|
| Conversion | fixN_from_int(v),
fixN_to_int(v),
fixN_from_float(v),
fixN_to_float(v) |
| Constants | fixN_ERR(), fixN_MAX(),
fixN_EPSILON() |
| Rounding | fixN_trunc(v), fixN_floor(v),
fixN_ceil(v), fixN_round(v) |
| Unary | fixN_abs(v), fixN_neg(v) |
| Math | fixN_sqrt(v), fixN_exp(v),
fixN_log(v), fixN_pow(a, b) |
| Trig | fixN_sin(v), fixN_cos(v) |
Where N is 32, 64,
128, or 256.
Cross-Width Conversions
Use @cast to convert between fixed-point
widths:
fix32:narrow = fix32_from_int(42);
fix64:wide = @cast<fix64>(narrow); // widening: lossless
fix32:back = @cast<fix32>(wide); // narrowing: may produce ERR on overflow
3. Dimensional Analysis
The compiler supports attaching SI units to any
fixN type at compile-time to prevent unit
mismatch errors (e.g., adding meters to seconds). *
fix64<Joules> *
fix64<Meters> *
fix128<Seconds> *
fix256<Newtons>
Usage Example:
fix64<Meters>:distance = 100.0fix64;
fix64<Seconds>:time = 9.58fix64;
// Compile error if you tried to assign `distance` to `time`!
fix64<Meters>:speed = distance / time; // fix64<Meters/Seconds>