feat(lang): float and double types with ordinary operators

`float` (32-bit) and `double` (64-bit) with + - * / %, comparisons and unary minus.
Decimal literals take their type from context and stay `fixed` elsewhere; int and long
promote implicitly (LUDIC_WARN_FLOAT_PROMOTE=1 lists every promotion). float(), double(),
int(), long() and fixed() convert; floats(n)/doubles(n) buffers; float fields, globals,
constants and parameters; Math.* computes in float for float arguments; string/print
write the shortest round-tripping decimal; float_bits/float_from_bits expose the bits.
@deterministic code may not use floats. The f_* runtime helpers stay as they are.

Editors know the new type words; the JetBrains plugin is 1.5.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-16 16:02:03 +03:00
parent f92d7f89c6
commit e010c2cecc
40 changed files with 67726 additions and 57272 deletions

View file

@ -191,7 +191,9 @@ for a complete title screen.
| `countdown` | an `int` component field the engine steps toward 0 once per Update | `i32` |
| a bare `enum` | its variants, as an `int` | `i32` |
| `IVec2` | an integer (x, y) pair by value — `v.x`, `v.y`, `IVec2.make/add/sub/…` | `i64` |
| `fixed` | Q16.16 fixed-point | `i32` |
| `fixed` | Q16.16 fixed-point — deterministic | `i32` |
| `float` | IEEE single-precision floating point | `float` |
| `double` | IEEE double-precision floating point | `double` |
| `bool` | boolean | `i32` |
| `entity` | entity handle | `i32` |
| `string` | text (a string literal, an interpolation, a concatenation) | `ptr` |
@ -201,6 +203,7 @@ for a complete title screen.
| `words` | buffer of 32-bit words — `w[i]` reads/writes an `int` | `ptr` |
| `fixeds` | buffer of `fixed` values — `f[i]` reads/writes a `fixed` | `ptr` |
| `pointers` | buffer of pointers — `p[i]` reads/writes a `pointer` | `ptr` |
| `floats` / `doubles` | buffer of floats / doubles — `floats(n)`, `v[i]` | `ptr` |
Allocate raw buffers with `bytes(n)` (n bytes) or `words(n)` (n 32-bit words);
both return a pointer you index with `buf[i]` — retype the binding (`words` /
@ -214,6 +217,44 @@ point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to
`fxmul`/`fxdiv`; mixing `int` and `fixed` promotes the `int`. Convert with
`fixed(i)` (int→fixed) and `floor(f)` (fixed→int).
### Floating point
`float` and `double` are ordinary IEEE numbers with ordinary operators, for
rendering, GPU data and any math that needs more range than `fixed`:
```ludic
program Shade {
function falloff(dist: float, radius: float) -> float {
let k = Math.clamp(1.0 - dist / radius, 0, 1)
return k * k
}
entry {
let light: float = falloff(2, 8) # ints promote to float
print(light * 0.5) # 0.28125
}
}
```
- **Literals take their type from context.** `1.5` is a `float` where a float is
expected — a typed binding, a parameter, a field, the other operand — and
exactly that decimal, not its Q16.16 approximation. With no float in sight it
stays `fixed`, so existing code keeps its meaning. A whole literal expression
(`1.0 / 3.0`) is evaluated in the context's type.
- **Promotion.** `int` and `long` promote to the float type of the other operand;
`float` with `double` promotes to `double`.
- **Explicit conversions.** `float(x)`, `double(x)`, `int(x)` (truncates toward
zero), `long(x)`, `fixed(x)` (truncated to Q16.16). `fixed` and the float types never
mix silently, and a `double` narrows to `float` only through `float(x)`.
- **`Math.*`** computes in float when given one (`Math.sqrt(2.0 * x)`) and answers
in that type — `Math.floor(x)` of a float is a float; `sign` returns `int`.
- **Text.** `string(x)`, `print(x)` and interpolation write the shortest decimal
that reads back as the same value: `0.3`, `2.0`, `0.30000000000000004`.
- **Bits.** `float_bits(x)` / `float_from_bits(i)` (and the `double_` pair) move
the IEEE pattern to and from an integer, for files and packets.
- **Determinism.** A `@deterministic` function or handler cannot compute with
floats — the compiler says so — because IEEE results can differ between
machines. Lockstep simulation stays in `fixed`.
## Properties, entities, queries
```ludic