Inside a number
There is one number type in mlog — double precision. Hence both the 53-bit limit and 0.1 + 0.2 ≠ 0.3
There is no separate integer type in mlog. Every number is a double-precision float, the
same double as in most languages: 64 bits, of which 52 hold the mantissa, 11 the exponent and
one the sign.
Everything that follows comes out of that arrangement: why integers are exact up to a certain
limit, why 0.1 + 0.2 is not 0.3, and which bits survive packing.
53 significant bits
Section titled “53 significant bits”The mantissa stores 52 bits plus one implied — 53 significant bits in all. While a number fits in them it is an exact integer; past that a gap opens up between neighbouring representable numbers.
| Number | What happens to it |
|---|---|
| up to 2⁵³ − 1 | stored exactly, bit for bit |
| 2⁵³ = 9,007,199,254,740,992 | the last integer before the gaps start |
| 2⁵³ + 1 | does not exist: rounds back to 2⁵³ |
| beyond | the step grows: first every other one, then every fourth, eighth… |
The example shows it directly: withOne is 2⁵² with a one mixed in, and the one is there.
lostBit is 2⁵³ with the same one, and it is not: check is zero, so the number stayed exactly
2⁵³.
Which bits are safe
Section titled “Which bits are safe”For bitwise operations the rule is simple:
- bits 0–52 are reliable. You can set them, read them, shift them — the number survives;
- bits 53–63 exist inside the operation but not in the variable. The game does bitwise operations on a 64-bit integer and puts the result back into a float — and the excess is rounded away. What gets lost are the low bits: the high ones stay in the number, and precision runs out from the bottom.
op shl tag 1 52op or packed packed tagThat is fine. But op shl tag 1 53 is not: the value will come back into the variable, only the
low bits will not be able to live next to it.
Fractions are stored no more precisely
Section titled “Fractions are stored no more precisely”A fractional number is stored in the same 53 bits, only now it is not “how many units” but “how
many binary fractions”. Decimal 0.1 in binary goes on forever — like 1/3 in decimal — so the
nearest representable value is what gets stored.
In the example fraction is 0.30000000000000004, and failure — the difference from a real
0.3 — is about 5.55 · 10⁻¹⁷.
Infinity and NaN do not fit in a variable
Section titled “Infinity and NaN do not fit in a variable”Division by zero gives infinity, the root of a negative number gives NaN. A variable stores neither: on assignment such a value turns into null.
op div bad 1 0bad will hold null, not “infinity”. So a division-by-zero check usually looks like a null
check after the operation — or a check of the divisor before it.
Unofficial fan project, not affiliated with Anuke. Mindustry sprites, fonts and translations © Anuke, used under GPL-3.0; Fira Code under OFL-1.1. Site code is GPL-3.0.