Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Yul (Solidity IR)
Zig
Javascript
GIMPLE
Ygen
sway
cmake source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
Options
Source code
cmake_minimum_required(VERSION 3.16) project(atto-pi) set(LLVM_MOS_PLATFORM c64) add_executable(atto-pi.prg atto-pi.c)
c++ source #2
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 12.5.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 13.4.0
ARM GCC 13.4.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 14.3.0
ARM GCC 14.3.0 (unknown-eabi)
ARM GCC 15.1.0
ARM GCC 15.1.0 (unknown-eabi)
ARM GCC 15.2.0
ARM GCC 15.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (ex-WINE)
ARM msvc v19.10 (ex-WINE)
ARM msvc v19.14 (ex-WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 12.4.0
ARM64 gcc 12.5.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 13.4.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 14.3.0
ARM64 gcc 15.1.0
ARM64 gcc 15.2.0
ARM64 gcc 4.9.4
ARM64 gcc 5.4
ARM64 gcc 5.5.0
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (ex-WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 12.5.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 13.4.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 14.3.0
AVR gcc 15.1.0
AVR gcc 15.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
BPF clang 20.1.0
BPF clang 21.1.0
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
EDG 6.7
EDG 6.7 (GNU mode gcc 14)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
HPPA gcc 14.3.0
HPPA gcc 15.1.0
HPPA gcc 15.2.0
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LoongArch64 clang (trunk)
LoongArch64 clang 17.0.1
LoongArch64 clang 18.1.0
LoongArch64 clang 19.1.0
LoongArch64 clang 20.1.0
LoongArch64 clang 21.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 13.4.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68K gcc 14.3.0
M68K gcc 15.1.0
M68K gcc 15.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
MinGW gcc 14.3.0
MinGW gcc 15.2.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 12.5.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 13.4.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 14.3.0
RISC-V (32-bits) gcc 15.1.0
RISC-V (32-bits) gcc 15.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 12.5.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 13.4.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 14.3.0
RISC-V (64-bits) gcc 15.1.0
RISC-V (64-bits) gcc 15.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 20.1.0
RISC-V rv32gc clang 21.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 20.1.0
RISC-V rv64gc clang 21.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 12.5.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 13.4.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC LEON gcc 14.3.0
SPARC LEON gcc 15.1.0
SPARC LEON gcc 15.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 12.5.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 13.4.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC gcc 14.3.0
SPARC gcc 15.1.0
SPARC gcc 15.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 12.5.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 13.4.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
SPARC64 gcc 14.3.0
SPARC64 gcc 15.1.0
SPARC64 gcc 15.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 12.5.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 13.4.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI C6x gcc 14.3.0
TI C6x gcc 15.1.0
TI C6x gcc 15.2.0
TI CL430 21.6.1
Tricore gcc 11.3.0 (EEESlab)
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
VAX gcc NetBSDELF 12.4.0 (Apr 16 05:27 2025)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 14.2.0 (20241119)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 14.2.0 (20241119)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 14.2.0 (20241119)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.41 VS17.11
arm64 msvc v19.42 VS17.12
arm64 msvc v19.43 VS17.13
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 20.1.0
armv7-a clang 21.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 20.1.0
armv8-a clang 21.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clad trunk (clang 21.1.0)
clad v1.10 (clang 20.1.0)
clad v1.8 (clang 18.1.0)
clad v1.9 (clang 19.1.0)
clad v2.00 (clang 20.1.0)
clad v2.1 (clang 21.1.0)
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
ez80-clang 15.0.0
ez80-clang 15.0.7
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 12.5.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 13.4.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
loongarch64 gcc 14.3.0
loongarch64 gcc 15.1.0
loongarch64 gcc 15.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips clang 20.1.0
mips clang 21.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 12.5.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 13.4.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 14.3.0
mips gcc 15.1.0
mips gcc 15.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 12.5.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 13.4.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 14.3.0
mips64 (el) gcc 15.1.0
mips64 (el) gcc 15.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 clang 20.1.0
mips64 clang 21.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 12.5.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 13.4.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 14.3.0
mips64 gcc 15.1.0
mips64 gcc 15.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mips64el clang 20.1.0
mips64el clang 21.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel clang 20.1.0
mipsel clang 21.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 12.5.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 13.4.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 14.3.0
mipsel gcc 15.1.0
mipsel gcc 15.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 12.5.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 13.4.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 14.3.0
power gcc 15.1.0
power gcc 15.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 12.4.0
power64 gcc 12.5.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 13.4.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc 14.3.0
power64 gcc 15.1.0
power64 gcc 15.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 12.5.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 13.4.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 14.3.0
power64le gcc 15.1.0
power64le gcc 15.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
qnx 8.0.0
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 12.5.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 13.4.0
s390x gcc 14.1.0
s390x gcc 14.2.0
s390x gcc 14.3.0
s390x gcc 15.1.0
s390x gcc 15.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 12.5.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 13.4.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 14.3.0
sh gcc 15.1.0
sh gcc 15.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (ex-WINE)
x64 msvc v19.10 (ex-WINE)
x64 msvc v19.14 (ex-WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.41 VS17.11
x64 msvc v19.42 VS17.12
x64 msvc v19.43 VS17.13
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (ex-WINE)
x86 msvc v19.10 (ex-WINE)
x86 msvc v19.14 (ex-WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.41 VS17.11
x86 msvc v19.42 VS17.12
x86 msvc v19.43 VS17.13
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.11
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86 nvc++ 25.1
x86 nvc++ 25.3
x86 nvc++ 25.5
x86 nvc++ 25.7
x86 nvc++ 25.9
x86-64 Zapcc 190308
x86-64 clang (-fimplicit-constexpr)
x86-64 clang (Chris Bazley N3089)
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental P3385)
x86-64 clang (experimental P3776)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (old concepts branch)
x86-64 clang (p1974)
x86-64 clang (pattern matching - P2688)
x86-64 clang (reflection - C++26)
x86-64 clang (reflection - TS)
x86-64 clang (resugar)
x86-64 clang (string interpolation - P3412)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 19.1.0
x86-64 clang 19.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 20.1.0
x86-64 clang 20.1.0 (assertions)
x86-64 clang 21.1.0
x86-64 clang 21.1.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 clang rocm-6.0.2
x86-64 clang rocm-6.1.2
x86-64 clang rocm-6.2.4
x86-64 clang rocm-6.3.3
x86-64 clang rocm-6.4.0
x86-64 clang rocm-7.0.1
x86-64 gcc (P2034 lambdas)
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 12.5
x86-64 gcc 12.5 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 13.4
x86-64 gcc 13.4 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 14.3
x86-64 gcc 14.3 (assertions)
x86-64 gcc 15.1
x86-64 gcc 15.1 (assertions)
x86-64 gcc 15.2
x86-64 gcc 15.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
x86-64 icx 2024.1.0
x86-64 icx 2024.2.0
x86-64 icx 2024.2.1
x86-64 icx 2025.0.0
x86-64 icx 2025.0.1
x86-64 icx 2025.0.3
x86-64 icx 2025.0.4
x86-64 icx 2025.1.0
x86-64 icx 2025.1.1
x86-64 icx 2025.2.0
x86-64 icx 2025.2.1
x86-64 icx 2025.2.1
z180-clang 15.0.0
z180-clang 15.0.7
z80-clang 15.0.0
z80-clang 15.0.7
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.12.1
zig c++ 0.13.0
zig c++ 0.14.0
zig c++ 0.14.1
zig c++ 0.15.1
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
/* * pi.c - Bellard's decimal pi spigot algorithm implementation * * Author: John Byrd <johnwbyrd at gmail dot com> * * See README.md for overview of the algorithm and mathematical background */ #include <assert.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> /* * Precision scaling system: MAX_DIGITS_LOG10 determines maximum supported * digit count as 10^MAX_DIGITS_LOG10. Types are automatically selected to * handle the specified order of magnitude without overflow. */ #ifndef MAX_DIGITS_LOG10 #define MAX_DIGITS_LOG10 5 // Default: up to 10^5 = 100,000 digits #endif #if MAX_DIGITS_LOG10 > 9 #error "MAX_DIGITS_LOG10 > 9 (billion digits) requires 128-bit integer support" #endif /* * Arbitrary-precision numbers represented as byte arrays in little-endian * format. Lower indices contain fractional part, highest index contains integer * part. Precision window [precision_lower, precision_upper] tracks active * computation range. */ typedef uint8_t *bignum; /* * Semantic type system based on algorithmic purpose rather than bit width. * Types are defined by their mathematical role in Bellard's algorithm, * allowing implementers to understand constraints and optimize for their * platform. */ /* - Digit specification and output */ /* Target and actual digit counts - the fundamental constraint that drives all * other requirements. Current: uint16_t allows ~65,535 digits max due to * iteration counter limits. Platforms: 8-bit systems practical max ~50,000 * digits (memory), 16-bit+ can use uint32_t for millions. */ #if MAX_DIGITS_LOG10 <= 4 typedef uint16_t digit_count_t; #elif MAX_DIGITS_LOG10 <= 9 typedef uint32_t digit_count_t; #else typedef uint64_t digit_count_t; #endif /* 3-digit output progression counter (0,3,6,9,12...) tracking total digits * produced. Range: 0 to (digit_count_t * 3). Must handle digit_count * 3 * without overflow. Current: uint16_t sufficient for current digit_count_t * limits. */ #if MAX_DIGITS_LOG10 <= 4 typedef uint16_t digit_progression_t; #elif MAX_DIGITS_LOG10 <= 9 typedef uint32_t digit_progression_t; #else typedef uint64_t digit_progression_t; #endif /* Individual 3-digit output values extracted from bignum integer part. * Range: 0-999 (three decimal digits). Only needs to hold 10-bit values. * Current: uint32_t provides safe headroom and matches *(uint32_t*) extraction. */ typedef uint32_t digit_value_t; /* - Iteration mathematics */ /* 4n iteration counter for Bellard's formula denominators (4n+1), (4n+3). * Range: 0 to (digit_count * 4/3), steps of 4. At 50,000 digits: max ~66,664. * Used in expressions: (4n+1)*8, (4n+3)*256. * Current: uint32_t handles all realistic digit counts. */ #if MAX_DIGITS_LOG10 <= 6 typedef uint32_t iter_4n_t; #else typedef uint64_t iter_4n_t; #endif /* 10n iteration counter for denominators (10n+1), (10n+3), (10n+5), (10n+7), * (10n+9). Range: 0 to (digit_count * 10/3), steps of 10. At 50,000 digits: max * ~166,660. Used in largest expression: (10n+9)*256 = ~42.7M at max digits. * Current: uint64_t prevents overflow in all denominator calculations. */ typedef uint64_t iter_10n_t; /* - Precision window management */ /* Precision window bounds [precision_lower, precision_upper] for bignum * optimization. Range: 0 to bignum_size-1, but can be negative during * comparisons. precision_lower: grows ~1.24x per iteration. precision_upper: * shrinks as numerator rescales. Current: int16_t signed to prevent comparison * bugs on 16-bit systems. */ #if MAX_DIGITS_LOG10 <= 4 typedef int16_t precision_bound_t; #elif MAX_DIGITS_LOG10 <= 9 typedef int32_t precision_bound_t; #else typedef int64_t precision_bound_t; #endif /* Fixed-point precision tracker (scaled by 128 for integer math). * Range: 0 to ~(digit_count * 159). Growth: +159 per iteration. * Formula: precision_lower = precision_base / 128. This optimization * signficantly reduces computation time in later iterations. * Current: int32_t handles growth rate across all practical digit counts. */ #if MAX_DIGITS_LOG10 <= 7 typedef int32_t precision_base_t; #else typedef int64_t precision_base_t; #endif /* Guard digits for computational headroom preventing accumulation errors. * Range: typically 3-10. More guard digits = more memory but safer computation. * Current: uint8_t sufficient for any reasonable guard count. */ typedef uint8_t guard_count_t; /* - Bignum array management */ /* Total size of each bignum array in bytes. * Formula: (digits * 415241) / 1000000 + guard_digits. * Range: ~418 bytes at 1K digits to ~20,765 bytes at 50K digits. * Current: uint16_t limits to 65,535 bytes - use uint32_t for larger scales. */ #if MAX_DIGITS_LOG10 <= 4 typedef uint16_t array_size_t; #elif MAX_DIGITS_LOG10 <= 9 typedef uint32_t array_size_t; #else typedef uint64_t array_size_t; #endif /* Array indices for bignum operations. * Range: 0 to array_size_t-1. Signed prevents index comparison bugs. * Current: int16_t matches precision_bound_t for consistency. */ #if MAX_DIGITS_LOG10 <= 4 typedef int16_t array_index_t; #elif MAX_DIGITS_LOG10 <= 9 typedef int32_t array_index_t; #else typedef int64_t array_index_t; #endif /* - Division engine arithmetic */ /* Denominators in Bellard's seven-term formula. * Range: (10n+1) minimum to (10n+9)*256 maximum. At 50K digits: max ~42.7M. * Must handle all denominator expressions: (10n+k)*scale_factor. * Current: uint64_t prevents overflow in largest expressions. */ typedef uint64_t divisor_t; /* Running remainder during binary long division. * Range: 0 to divisor-1 during computation. Must match divisor_t capacity. * Current: uint64_t matches divisor_t requirements. */ typedef uint64_t remainder_t; /* Individual quotient byte constructed during 8-bit division. * Range: 0-255 (single byte result from binary long division). * Current: uint16_t provides headroom for intermediate calculations. */ typedef uint16_t quotient_byte_t; /* - Carry propagation arithmetic */ /* Carry values during multiply-by-250 and multiply-by-1000 operations. * Range: 0 to max(255*250, 255*1000) = 255,000. * Must handle: byte * constant + previous_carry. * Current: uint32_t handles worst case with headroom. */ typedef uint32_t carry_t; /* Temporary storage for intermediate arithmetic (byte * constant). * Range: 0 to 255*1000 = 255,000. Used in: temp = byte * multiplier + carry. * Current: uint32_t matches carry_t for consistent intermediate calculations. */ typedef uint32_t temp_arith_t; /* Signed arithmetic for add/subtract operations with borrow/carry. * Range: -255 to 510 (byte - byte, or byte + byte + carry). * Must handle negative results during subtraction operations. * Current: int32_t provides safe range for all bignum arithmetic. */ typedef int32_t signed_arith_t; /* - Algorithm control */ /* Operation toggle for alternating series (1=add, 0=subtract). * Range: 0 or 1 only. Controls alternating signs in Bellard's series. * Current: uint8_t minimal storage for boolean-like value. */ typedef uint8_t operation_toggle_t; /* Sign control parameter for bignum_div_addsub function. * Range: 0 (add) or non-zero (subtract). Treated as boolean in conditionals. * Current: int matches function parameter conventions. */ typedef int sign_control_t; /* - Bignum initialization */ /* Small integer coefficients used to initialize bignums. * Range: Small positive integers (typically 4 for Bellard's coefficient). * Used in bignum_set() to establish initial values. * Current: uint32_t allows 32-bit write optimization at bignum high end. */ typedef uint32_t bignum_init_value_t; /* * Lightweight printing functions that avoid printf overhead (~3KB+ on 6502). * Critical for fitting within memory constraints of retro systems. */ /* Print null-terminated string character by character. */ void print_str(const char *s) { while (*s) putchar(*s++); } /* Print exactly 3 digits with trailing space, used for main digit output. */ void print_three_digit(digit_value_t val) { putchar('0' + (val / 100) % 10); putchar('0' + (val / 10) % 10); putchar('0' + val % 10); putchar(' '); } /* Print unsigned integer without leading zeros. */ void print_uint(digit_count_t val) { if (val == 0) { putchar('0'); return; } char buf[12]; // Enough for uint32_t max (4,294,967,295 = 10 digits) + safety int i = 0; do { buf[i++] = '0' + (val % 10); val /= 10; } while (val); while (i--) putchar(buf[i]); } /* Initialize platform-specific optimizations, expanding heap on LLVM-MOS * targets. */ void init_platform() { #ifdef __mos__ // __set_heap_limit(__get_heap_max_safe_size()); #endif } /* Global variables for Bellard's pi calculation algorithm. * These maintain state across all bignum operations and track the * precision boundaries as the calculation progresses. Understanding * these variables is crucial for comprehending the algorithm's operation. */ /* Divisor used in bignum_div_addsub() for each term of Bellard's formula. * Ranges from small values like (10n+1) to large products like (10n+9)*256. * The seven terms use different polynomial denominators: * - Terms with 10n: (10n+1), (10n+3)*4, (10n+5)*64, (10n+7)*64, (10n+9)*256 * - Terms with 4n: (4n+1)*8, (4n+3)*256 * Must be 64-bit to handle the largest denominators without overflow. * At 50,000 digits: largest value ~= (166,670+9)*256 = 42,671,744 */ divisor_t divisor; /* Lower index bound for active bignum precision window. * Represents the leftmost (least significant) byte still containing * meaningful fractional data. As iterations progress, the fractional * precision requirements grow, and this bound increments to skip * computation on bytes that have become insignificant (leading zeros). * Formula: precision_lower = base / 128, where base grows by 159 per iteration. * This optimization significantly reduces computation time in later iterations. */ precision_bound_t precision_lower; /* Upper index bound for active bignum precision window. * Represents the rightmost (most significant) byte containing non-zero * numerator data. The numerator is rescaled by 250/256 each iteration, * causing it to shrink and making higher-order bytes become zero. * This bound decrements accordingly, reducing unnecessary computation. * The precision window [precision_lower, precision_upper] defines the * active computation range, optimizing performance as the calculation * progresses. */ precision_bound_t precision_upper; /* Size of each bignum array in bytes, calculated from requested digits. * Formula: (digits * 415241) / 1000000 + guard_digits * The constant 0.415241 = log_2(10)/8, representing bytes needed per decimal * digit. For 50,000 digits: ~= 20,762 + 3 = 20,765 bytes per bignum. This * provides sufficient binary precision to accurately represent the requested * decimal precision without accumulation errors. */ array_size_t bignum_size; /* Sum accumulator bignum storing the running total of all Bellard formula * terms. Structure: [fractional_part...][fractional_part][integer_part] * - Integer part (highest byte): Contains 3 digits ready for extraction * - Fractional part (lower bytes): Accumulates precision for future iterations * After each iteration, integer digits are extracted and masked to zero, * then the entire sum is multiplied by 1000 to shift the next 3 digits * from fractional to integer position. */ bignum sum_accumulator; /* Numerator bignum used in division operations for each formula term. * Initialization: Set to 4 (derived from Bellard's series coefficient) * Per-iteration cycle: * 1. Divided by each of the 7 term denominators via bignum_div_addsub() * 2. Rescaled by factor 250/256 to prevent unbounded growth * The 250/256 factor = 1000/1024. This rescaling maintains the mathematical * precision balance between sum_accumulator and numerator across iterations. */ bignum numerator; /* * Bignum arithmetic functions * These functions implement arbitrary-precision arithmetic operations * on byte arrays representing fixed-point numbers in little-endian format. */ /* Initialize a bignum to a small integer value, clearing the fractional part */ void bignum_set(bignum bn, bignum_init_value_t value); /* Perform division and add or subtract the quotient into the sum bignum. * This implements the core mathematical operation for each term in Bellard's * formula */ void bignum_div_addsub(sign_control_t is_subtract); /* Rescale a bignum by the factor 250/256, used to prevent numerator overflow * while maintaining precision across iterations */ void bignum_rescale(bignum bn); /* Zero the integer part of a bignum, removing digits that have been output */ void bignum_mask_digits(bignum bn); /* Multiply a bignum by 250 with carry propagation. * Part of the rescaling operation (250/256 ratio) */ void bignum_multiply_250(bignum bn); /* Multiply a bignum by 1000 with carry propagation. * Shifts the next three decimal digits into the integer position */ void bignum_multiply_1000(bignum bn); /* * Memory management functions * Handle allocation and sizing of bignum arrays based on precision requirements */ /* Calculate the required size for bignums based on desired digit precision */ array_size_t calculate_bignum_size(digit_count_t digits, guard_count_t guard); /* Allocate and initialize both sum and numerator bignums */ void allocate_bignums(array_size_t size); /* Free allocated bignum memory */ void deallocate_bignums(void); /* * Main algorithm */ /* Execute Bellard's formula to calculate pi to the specified precision */ void calculate_pi(digit_count_t digits, guard_count_t guard_digits); /* * Initialize a bignum to a small integer value by clearing the fractional * part and placing the integer at the highest position. The integer value * is written as a 32-bit word at the high end, which may span multiple bytes. * This is used to set initial coefficients like 4 for the Bellard formula. */ void bignum_set(bignum bn, bignum_init_value_t value) { /* Clear fractional portion from precision_lower to bignum_size-1 */ for (array_index_t i = precision_lower; i < bignum_size; i++) { bn[i] = 0; } /* Place integer value at highest position (32-bit write) */ *(bignum_init_value_t *)&bn[bignum_size - 1] = value; } /* * Core division and accumulation function implementing each term of Bellard's * formula. Performs: sum_accumulator +/- = (numerator / divisor) * * Algorithm: Binary long division processing 8 bits at a time * 1. For each byte position (most significant to least significant): * - Build remainder by shifting left 8 bits and adding next numerator byte * - Scale divisor by 256 to match remainder magnitude * - Extract quotient bits one by one using comparison and subtraction * 2. Add or subtract resulting quotient byte into sum_accumulator with carry * propagation * * The is_subtract parameter controls whether this term is added or subtracted, * implementing the alternating signs in Bellard's series. */ void bignum_div_addsub(sign_control_t is_subtract) { remainder_t remainder = 0; /* Running remainder for division */ divisor_t divisor_original = divisor; /* Preserve original divisor */ /* Process bignum from most significant to least significant byte */ for (array_index_t i = precision_upper; i >= precision_lower; i--) { #ifdef BRANCH_PREDICTING_CPU /* Skip byte entirely if no contribution possible. * This optimization helps on modern CPUs with branch prediction * but may hurt performance on simple processors like 6502. */ if (remainder == 0 && numerator[i] == 0) { continue; /* quotient_byte would be 0, nothing to add/subtract */ } #endif /* Build up remainder: shift left 8 bits and add next byte */ remainder = remainder * 256 + numerator[i]; quotient_byte_t quotient_byte = 0; /* Quotient byte being constructed */ divisor *= 256; /* Scale divisor to match remainder */ /* Binary division: extract 8 bits one at a time */ for (uint8_t j = 0; j <= 7; j++) { quotient_byte *= 2; /* Shift quotient bit left */ divisor /= 2; /* Halve divisor for this bit */ if (remainder >= divisor) { remainder -= divisor; /* Remainder -= divisor */ quotient_byte += 1; /* Set quotient bit */ } } /* Add or subtract quotient byte into sum with carry propagation */ array_index_t sum_idx = i; do { signed_arith_t tmp = is_subtract ? (sum_accumulator[sum_idx] - quotient_byte) : (sum_accumulator[sum_idx] + quotient_byte); sum_accumulator[sum_idx++] = tmp & 0xff; /* Store low byte */ /* Carry/borrow continues if result outside byte range */ quotient_byte = (tmp >= 0 && tmp <= 0xff) ? 0 : 1; } while (quotient_byte); } /* Restore original divisor for next term */ divisor = divisor_original; } /* * Rescale bignum by factor 250/256 = 1000/1024 to prevent numerator growth. * This implements the radix correction factor in Bellard's decimal adaptation. * * Two-step process: * 1. Multiply by 250 using carry propagation * 2. Divide by 256 by shifting all bytes down one position (equivalent to >> 8) * * The 250/256 ratio compensates for the approximation 10^3 ~= 2^10 used in * adapting Bellard's binary series for decimal digit extraction. */ void bignum_rescale(bignum bn) { bignum_multiply_250(bn); /* Multiply by 250 */ /* Divide by 256: shift all bytes down one position */ for (array_index_t i = precision_lower; i < bignum_size; i++) { bn[i] = bn[i + 1]; } bn[bignum_size] = 0; /* Clear vacated high byte */ } /* * Zero the integer part of bignum after extracting output digits. * This removes the digits that have been output, leaving only the * fractional part for the next iteration. Clears both the integer * byte and one extra byte for safety. */ void bignum_mask_digits(bignum bn) { array_index_t i = bignum_size - 1; bn[i] = 0; /* Clear integer part */ bn[i + 1] = 0; /* Clear extra byte for safety */ } /* * Multiply bignum by 250 with carry propagation. * This is part of the 250/256 rescaling factor. Each byte is multiplied * by 250, with carries propagated to higher bytes. The final carry must * be zero to ensure no precision loss (guaranteed by proper sizing). */ void bignum_multiply_250(bignum bn) { temp_arith_t temp; carry_t carry = 0; /* Process from least to most significant byte */ for (array_index_t i = precision_lower; i <= bignum_size; i++) { temp = (temp_arith_t)bn[i] * 250 + carry; bn[i] = temp & 0xff; /* Store low byte */ carry = temp >> 8; /* Propagate high byte as carry */ } /* Verify no final carry (indicates sufficient precision) */ if (carry > 0) { assert(0 && "bignum_multiply_250 should end with carry = 0"); } } /* * Multiply bignum by 1000 to shift next three decimal digits into position. * After extracting three digits from the integer part, this operation moves * the next three digits from the fractional part into the integer part for * the following iteration. Uses carry propagation like multiply_250. */ void bignum_multiply_1000(bignum bn) { temp_arith_t temp; carry_t carry = 0; /* Process from least to most significant byte */ for (array_index_t i = precision_lower; i <= bignum_size; i++) { temp = (temp_arith_t)bn[i] * 1000 + carry; bn[i] = temp & 0xff; /* Store low byte */ carry = temp >> 8; /* Propagate high byte as carry */ } /* Verify no final carry (indicates sufficient precision) */ if (carry > 0) { assert(0 && "bignum_multiply_1000 should end with carry = 0"); } } /* * Main implementation of Bellard's formula for calculating pi. * Generates digits using a spigot algorithm that produces three decimal * digits per iteration through an alternating series of rational terms. */ void calculate_pi(digit_count_t digits, guard_count_t guard_digits) { /* Initialize memory and precision tracking */ bignum_size = calculate_bignum_size(digits, guard_digits); allocate_bignums(bignum_size); /* Set up bignum array bounds: track precision window that shrinks * as calculation progresses. Base tracks fractional precision growth * in fixed-point arithmetic (scaled by 128 for integer math). */ precision_base_t base = 0; /* Precision tracker (*128) */ precision_lower = 0; /* Lower bound starts at 0 */ precision_upper = bignum_size - 1; /* Upper bound at array end */ /* Initialize numerator to 4 (coefficient from Bellard's formula) */ bignum_set(numerator, 4); /* Initialize loop counters based on iteration number n: * three_n = 3n (0, 3, 6, 9, 12, ...) * four_n = 4n (0, 4, 8, 12, 16, ...) * ten_n = 10n (0, 10, 20, 30, 40, ...) */ digit_progression_t three_n = 0; /* 3n counter - tracks total digits produced */ iter_4n_t four_n = 0; /* 4n counter - used in denominators 4n+1, 4n+3 */ iter_10n_t ten_n = 0; /* 10n counter - used in denominators 10n+1, 10n+3, 10n+5, 10n+7, 10n+9 */ operation_toggle_t op = 1; /* Operation toggle: 1=add, 0=subtract */ /* Initialize sum accumulator to 4 */ bignum_set(sum_accumulator, 4); digit_count_t digit_counter = 0; /* Track digits output for reporting */ /* Main calculation loop: each iteration produces 3 digits */ do { /* Compute the seven terms of Bellard's decimal spigot formula: * Each term has form: coefficient / (polynomial_in_n * scale_factor) * The alternating signs and specific denominators derive from Bellard's * adaptation of his binary BBP formula for decimal digit extraction. */ divisor = (four_n + 1) * 8; bignum_div_addsub(op); divisor = (four_n + 3) * 256; bignum_div_addsub(op); divisor = ten_n + 1; if (three_n > 0) { /* Skip first iteration to avoid double-counting initial 4 */ bignum_div_addsub(1 - op); } divisor = (ten_n + 3) * 4; bignum_div_addsub(op); divisor = (ten_n + 5) * 64; bignum_div_addsub(op); divisor = (ten_n + 7) * 64; bignum_div_addsub(op); divisor = (ten_n + 9) * 256; bignum_div_addsub(1 - op); /* Extract three digits from sum's integer part */ digit_value_t digit_val = *(digit_value_t *)&sum_accumulator[bignum_size - 1]; /* Output digits with appropriate formatting */ if (three_n > 0) { print_three_digit(digit_val); digit_counter += 3; /* Each iteration produces 3 digits */ } else { print_uint(digit_val); putchar('.'); putchar('\n'); /* First digit with decimal */ digit_counter += 1; /* First iteration produces 1 digit */ } /* Prepare for next iteration */ bignum_mask_digits(sum_accumulator); /* Remove extracted digits */ bignum_multiply_1000(sum_accumulator); /* Shift next 3 digits up */ bignum_rescale(numerator); /* Apply 250/256 scaling */ /* Toggle operation sign for alternating series */ op = 1 - op; /* Adjust precision tracking as numerator shrinks from rescaling */ if (numerator[precision_upper] == 0) { precision_upper = precision_upper - 1; } /* Update loop variables for next iteration. */ base += 159; precision_lower = base / 128; /* Convert to byte index */ four_n += 4; /* Advance 4n counter: 0->4->8->12... */ ten_n += 10; /* Advance 10n counter: 0->10->20->30... */ three_n += 3; /* Advance 3n counter: 0->3->6->9... */ } while (digit_counter < digits); /* Clean up and report results */ deallocate_bignums(); print_str("\nCalculated "); print_uint(digit_counter); print_str(" digits of pi.\n"); } /* * Each decimal digit requires log2(10) bits of binary precision, * approximately 3.321928. For d decimal digits, the total bits needed equal * d times log2(10). The bytes needed equal that total divided by 8, or * approximately d times 0.415241. * * Bellard's series converges such that higher digits require more precision. * The bignum must maintain enough fractional bits to avoid accumulation * errors that compound across iterations. When M, the precision upper bound, * drops below L, the precision lower bound, divisions cease, leading to * stagnant or zero output. * * The constant 0.4152410118609203 is precomputed as log2(10) divided by 8. * No runtime log2() call is made, which is important for the 6502 target * without a math library. Size includes padding for safe 32-bit reads during * digit extraction. Memory scales linearly, requiring approximately 20,765 * bytes for 50,000 digits compared to the old formula's approximately 20,836. */ array_size_t calculate_bignum_size(digit_count_t digits, guard_count_t guard) { return (array_size_t)((digits * 415241LL) / 1000000 + guard); } /* * Allocate memory for both bignum arrays with error checking. * Includes padding bytes to ensure safe 32-bit read operations when * extracting output digits. Initializes all bytes to zero and exits * with error message if allocation fails. */ void allocate_bignums(array_size_t size) { const array_size_t pad = 4; /* Padding for safe 32-bit reads */ /* Allocate sum accumulator with error checking */ sum_accumulator = (bignum)malloc(size + pad); if (!sum_accumulator) { print_str("Failed to alloc sum_accumulator: "); print_uint(size + pad); exit(1); } /* Allocate numerator with error checking */ numerator = (bignum)malloc(size + pad); if (!numerator) { print_str("Failed to alloc numerator: "); print_uint(size + pad); free(sum_accumulator); /* Clean up partial allocation */ exit(1); } /* Initialize both arrays to zero */ for (array_index_t i = 0; i < size + pad; i++) { sum_accumulator[i] = 0; numerator[i] = 0; } } /* * Free allocated bignum memory. * Called at end of calculation to prevent memory leaks. */ void deallocate_bignums(void) { free(sum_accumulator); free(numerator); } int main(void) { digit_count_t digits = 100; guard_count_t guard = 3; /* Initialize platform-specific heap expansion */ init_platform(); print_str("Calculating pi to "); print_uint(digits); print_str("+ digits...\n"); calculate_pi(digits, guard); return 0; }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree