A number library for MoonBit
Numbers that say what they are.
Arbitrary-precision integers, IEEE-754 floats printed honestly, complex
numbers written 3+4j, and exact fractions. One package, one
error type, one formatting language.
Four kinds, one tower
| Kind | Type | What it is |
|---|---|---|
| Integer | @bigint.BigInt |
Core's arbitrary-precision integer, with what it is missing: floor
division, a modulo that takes the divisor's sign, modular powers,
isqrt, comb, perm, bytes. |
| Float | Double |
Binary64, read and written so that the text is the shortest one that reads back — and the exact value one query away. |
| Complex | Complex |
{ re, im } of doubles, with the signed-zero,
infinity and NaN rules written down rather than hoped for. |
| Fraction | Fraction |
{ num, den }, always in lowest terms with a positive
denominator, so arithmetic never drifts. |
Number holds any of the four and does the mixing, so a
literal, a ratio and a float can meet in one expression:
let third = @moonnum.parse("1/3")
@moonnum.parse("1") + third
@moonnum.repr_float(0.1 + 0.2)
4/3
0.30000000000000004
Reading and writing
Every parser takes the spellings you already type: base prefixes,
underscores between digits, surrounding whitespace, inf and
nan in any case, 1+2j and 3+5i.
Refusals carry the offset where the text stopped making sense.
@moonnum.parse_int("0xff_ec", base=0)
@moonnum.parse_float("1_000.5e-2")
@moonnum.parse_complex("(3+4j)")
@moonnum.parse_fraction("-1.25")
65516
10.005
(3+4j)
-5/4
Writing is the other half, and the half that is usually wrong.
repr_float gives the shortest string that reads back as the
same double, and switches to an exponent exactly where the digits would
start lying.
@moonnum.repr_float(1.0e15)
@moonnum.repr_float(1.0e16)
@moonnum.repr_float(1.0e-5)
@moonnum.float_hex(0.1)
@moonnum.repr_complex(@moonnum.parse_complex("-0-1j"))
1000000000000000.0
1e+16
1e-05
0x1.999999999999ap-4
(-0-1j)
One formatting language
Fill, alignment, sign, z, #, zero padding,
width, grouping and precision, with the presentation types
b c d e E f F g G n o x X %. The same specifier works on
every kind, including the exact ones.
| Spec | Value | Result |
|---|---|---|
015,d | 1234567 | 000,001,234,567 |
#010_x | 255 | 0x000_00ff |
*^12.2f | 3.14159 | ****3.14**** |
.3% | 1/3 | 33.333% |
+.1e | 1.5 | +1.5e+00 |
z | -0.0 | 0.0 |
Zero padding participates in grouping, z acts before the
sign is chosen, and # keeps the zeros g would
drop. Those three sentences are the whole difference between a formatter
and a correct formatter, and each is a test.
Failure is a value
One error type, four cases, no panics. Division is a function rather than an operator, because a numeric library that aborts on zero is worse than one that says so.
pub(all) suberror Refused {
Malformed(at~ : Int, reason~ : String)
ZeroDivision
Overflow(reason~ : String)
Domain(reason~ : String)
}
text stopped making sense
at this UTF-16 offset
1/0, 1 % 0, Fraction(1, 0)
does not fit the target type
defined input, no answer
Start
moon add moonbitstack/moonnum
Then read the tutorial — five pages, one per kind plus the format language, each one runnable as written. The API reference lists every public item, and the design notes explain where the digits come from.