Arbitrary precision¶
Integers stay Python integers: when a native multiply overflows a 64-bit
word, the call goes back to CPython and still returns the exact answer.
cube(3) runs as three native multiplies. cube(10**7) overflows a word on
the second one, and the guard hands the call back to CPython, which prints
the 21-digit answer.
Run it¶
The overflow guard¶
Native code multiplies 64-bit words. Every +, -, and * on a plain
int lowers to LLVM's overflow-checking form. A set flag means the true
value no longer fits, so the function returns to its Python body with the
original arguments and computes there. The native path does not answer with
a wrapped number: it either has the right one or steps aside.
ppy build keeps the same guards, so an artifact answers as ppy run
does. --unsafe, on either command, drops them. The code then wraps at 64
bits like every native compiler's output. That is the 10 ms between
ppy run and ppy build --unsafe on the README's collatz kernel.
Floor division and the sign of the remainder¶
floor_and_mod(-7, 2) is -4 + 1, and floor_and_mod(7, -2) is
-4 + -1: // rounds toward negative infinity and % takes the sign of
the divisor. C's truncating division would give -3. The IR carries
rounding = "floor" on the operation, and the LLVM backend emits the
sign-corrected sequence.
Division by zero is a guard as well: divide(1, 0) raises
ZeroDivisionError from the Python body.
What it prints¶
python arbitrary_precision.ppy, ppy arbitrary_precision.ppy, ppy run arbitrary_precision.ppy
arbitrary_precision.ppy is hand-written; there is no .py source and no
conversion step.
02_arbitrary_precision/arbitrary_precision.ppy¶
import ppy
@ppy.pure
def cube(x: int) -> int:
return x * x * x
@ppy.pure
def floor_and_mod(a: int, b: int) -> int:
return a // b + a % b
@ppy.pure
def divide(a: int, b: int) -> float:
return a / b
def main() -> None:
print(cube(3))
print(cube(10**7))
print(floor_and_mod(-7, 2), floor_and_mod(7, -2))
try:
divide(1, 0)
except ZeroDivisionError as error:
print("caught", type(error).__name__)
if __name__ == "__main__":
main()
Source: examples/02_arbitrary_precision.