The integer that never runs out
MoonBit's BigInt is already arbitrary-precision. What it does
not have is the set of operations a numeric programmer reaches for, and the
spellings that make them usable. This page is those.
Division that agrees with itself
a // b rounds towards negative infinity and a % b
takes the sign of b, so the two always satisfy
a == (a // b) * b + a % b. Truncating division, the usual
default, breaks that identity for negative numbers and quietly returns a
remainder with the wrong sign.
for (a, b) in [(7N, 2N), (-7N, 2N), (7N, -2N), (-7N, -2N)] {
let (q, r) = @moonnum.divmod(a, b)
println("\{a} / \{b} = \{q} rem \{r}")
}
7 / 2 = 3 rem 1
-7 / 2 = -4 rem 1
7 / -2 = -4 rem -1
-7 / -2 = 3 rem -1
Size is the only limit
There is no overflow to guard against, which means the interesting cases are the ones that used to need a library: factorials, binomial coefficients, integer square roots of very large numbers.
@moonnum.factorial(50N)
@moonnum.power(2N, 200N)
@moonnum.comb(@moonnum.parse_int("100"), 50N)
@moonnum.isqrt(@moonnum.power(10N, 40N))
30414093201713378043612608166064768844377641568960512000000000000
1606938044258990275541962092341162602522202993782792835301376
100891344545564193334812497256
100000000000000000000
Powers, with and without a modulus
power is the modular form as well as the plain one. With a
modulus and a negative exponent it computes the modular inverse, which is
what the extended Euclidean algorithm is for; when no inverse exists it
refuses instead of returning nonsense.
@moonnum.power(3N, -1N, modulus=7N)
@moonnum.power(2N, 1000N, modulus=97N)
@moonnum.power(2N, -1N, modulus=4N)
5
36
base is not invertible for the given modulus
Without a modulus, a negative exponent has no integer
result. power says so; if you want the float answer, ask
Number::pow, which follows the tower.
What a refusal prints. Refused has a
Show implementation, so the text above is
err.to_string(): the reason, and the offset when there was one.
ZeroDivision has no payload and prints as
division by zero.
Reading what people write
base~ = 0 reads the prefix. Underscores are allowed between
digits — not next to one — and surrounding whitespace is ignored. Everything
else is refused with the offset of the character that spoiled it.
@moonnum.parse_int("0xff_ec", base=0)
@moonnum.parse_int(" 1_000 ", base=0)
@moonnum.parse_int("0b1010", base=2)
@moonnum.parse_int("12x", base=0)
65516
1000
10
unexpected trailing character at offset 2
Bytes
Two's complement when you ask for it, big-endian unless you say otherwise, and an overflow when the value does not fit the length you promised.
@moonnum.to_bytes(-2N, length=4, signed=true)
@moonnum.of_bytes(bytes, signed=true)
@moonnum.to_bytes(256N, length=1)
ff ff ff fe
-2
int does not fit in the requested byte length