RedScript v2.5.0: Double Precision, N-Order Bezier & Stdlib Overhaul
RedScript v2.5.0: Double Precision, N-Order Bezier & Stdlib Overhaul
Today marks a milestone in RedScript development: v2.5.0 ships with a completely redesigned numeric type system, IEEE 754 double-precision arithmetic running inside Minecraft's scoreboard engine, N-order Bézier curves, and a massive stdlib expansion bringing the test suite from 1277 to 1485 cases. Here's what went into this release.
Type System Redesign
The first change is a naming clarification that's been overdue. The old float type — which was never truly floating-point — is now called fixed. It stores values as scoreboard integers scaled by ×10000, making it a 32-bit fixed-point type. The old name was actively misleading.
Alongside this rename, a brand new double type enters the language: a proper IEEE 754 64-bit float, backed not by the scoreboard but by NBT storage in the rs:d namespace. This is a fundamentally different beast — it lives in NBT, not scoreboards, and operations on it require dedicated function calls rather than scoreboard arithmetic.
Casting between the two is now explicit only. Writing x as double or x as fixed is required wherever a conversion occurs. The compiler will no longer silently coerce between numeric types. A new lint diagnostic, [FloatArithmetic], fires whenever the legacy float name appears in an arithmetic expression — a migration aid for existing codebases.
Double Arithmetic: MC Black Magic
This is the part that required the most creative engineering. Minecraft's scoreboard system operates on 32-bit integers. It has no native floating-point instructions. Yet entity coordinates — and NBT storage values of type double — are stored as IEEE 754 64-bit doubles internally by the JVM. The trick is to exploit that fact indirectly.
Addition: Entity Position Trick
Minecraft entities store their Pos array as doubles. The key insight is that loot spawn places entities at arbitrary coordinates with no range limits (unlike tp, which clamps to ±30,000,000). We spawn a marker entity and use relative teleport to perform addition:
# a + b:
$tp <marker> $(x) 0 0
# → teleport marker to x = a (injected via function macro)
$execute at <marker> run tp <marker> ~$(dx) 0 0
# → move marker by +b (relative, so final x = a + b)
data get entity <marker> Pos[0]
# → read the result: a + b as a doubleThe relative tp instruction (~) does the addition in double precision at the JVM level. We're using the game's own coordinate math as a floating-point ALU.
Multiplication: Function Macro Scale Injection
execute store requires its scale argument to be a compile-time literal — you can't pass a variable there. The workaround uses MC function macros to inject the scale dynamically at runtime:
execute store result storage rs:d __scale double 0.0001 \
run scoreboard players get $f __ns
# → __scale = f × 0.0001 (a double holding f/10000)
$execute store result storage rs:d out double $(scale) \
run data get storage rs:d input 1
# → out = input × $(scale) = d × (f/10000)By pre-computing __scale as a double and then using a macro to splice it into the execute store command, we get a fixed × double multiply without any compile-time constant folding.
Division: Display Entity SVD
Minecraft Display Entities have a transformation matrix that the game decomposes via SVD (Singular Value Decomposition) to extract translation, rotation, and scale. By constructing the matrix such that the scale component equals input / divisor, the game engine performs the division for us in hardware — effectively offloading a float divide to the renderer's matrix decomposition code.
Compiler Intrinsics
All of this machinery is transparent at the language level. Writing a + b where both a and b are double variables now auto-lowers to a double_add(a, b) call during MIR lowering. The compiler selects the right intrinsic based on operand types. No syntax changes required in user code.
New Stdlib Modules
parabola.mcrs
Ballistic trajectory math for projectile simulation:
- Compute initial velocity vector from source position, target displacement, and a desired flight time
- Evaluate position at time
tunder constant gravity - Drag simulation with configurable damping coefficient
quaternion.mcrs
Display Entity rotation uses quaternions internally. This module covers the full quaternion algebra needed to drive them:
- Quaternion multiply, conjugate, normalize
- SLERP interpolation for smooth rotation transitions
- Euler angles → quaternion conversion
One notable bug fixed here: the SLERP normalization was calling mulfix with a scale that produced a magnitude of approximately 31622 instead of 10000. The issue was a missing square root in the normalization step — the intermediate value was being used unsquared, inflating the scale by √10000 = 100.
bezier_quartic + bezier_n
N-order Bézier curves via De Casteljau's algorithm. bezier_n operates in-place on the control point array (destructive but fast). bezier_n_safe makes a copy first, preserving the original control points for repeated evaluation. Both functions accept a fixed-point parameter t ∈ [0, 10000].
bigint_mul / bigint_sq
Arbitrary-precision integer arithmetic using scoreboard arrays. bigint_mul multiplies two multi-limb integers; bigint_sq is an optimized squaring routine that exploits symmetry to roughly halve the number of multiply operations needed.
High-Precision Math
ln_hp: Newton-Raphson Refinement
The existing ln_5term function approximates ln(x) using a 5-term series, giving about 6 significant digits. ln_hp wraps it with one Newton-Raphson correction step:
y₀ = ln_5term(x) # initial estimate
correction = (x - exp(y₀)) × 10000 / exp(y₀)
y₁ = y₀ + correction # refined resultThe correction converges to machine precision in a single step for values near the initial estimate, pushing accuracy to 8–9 significant digits — enough for most physics simulations.
Statistical Distributions in signal.mcrs
Four new sampling functions join the signal processing module:
gamma_sample(k, θ)— sum-of-exponentials method for integer shape parameterkpoisson_sample(λ)— Knuth's algorithm (product of uniforms until < e^−λ)negative_binomial_sample(r, p)— compounded Poisson-gammageometric_sample(p)— closed form via inverse CDF:⌊ln(U) / ln(1−p)⌋
These enable particle systems, procedural generation, and probabilistic AI behaviors to run entirely within a datapack — no external tooling needed.
Numbers
| Metric | Before | After | Delta |
|---|---|---|---|
| Tests | 1277 | 1485 | +208 |
| Stdlib functions | — | ~40 new | — |
| Numeric types | float, int | fixed, double, int | +1 |
All commits in this release are GPG-signed. The changelog is in CHANGELOG.md at the repo root.
v2.5.0 represents the most significant change to RedScript's numeric model since the language began. Double precision unlocks a class of physics and rendering problems that were previously out of reach for datapacks. The stdlib expansion makes those building blocks available without reinventing the wheel on every project.
