graph TD H["fractions-cpp"] H --> EXT["extfractions.hpp\nExtFraction\nC++-native"] H --> PY["pyfractions.hpp\nFraction\nPython clone"] EXT --> E1["no exceptions\nconstexpr ctor\ntail-recursive gcd"] PY --> P1["throws on zero\nexplicit overflow guards\ndouble fallback"] style H fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style EXT fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style PY fill:#fff9c4,stroke:#f57f17,stroke-width:3px style E1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style P1 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px
Small 1/2, 1/3 · ns/op (lower is better) add 57.7 73.6 mul 28.9 72.2 div 28.9 66.2 add-assign 21.6 72.9
Large values · py / ext ratio (1.0 = parity) 1.0× add 1.38× sub 1.40× mul 2.28× div 2.44× div-assign 2.51×
graph LR PY["pyfractions\nslower"] PY --> G["overflow guards\ninteger division per op"] PY --> T["compound ops\ncopy + temp"] PY --> C["ctor throws\nblocks fast path"] G --> FIX["→ mul-overflow check"] T --> KEEP["design choice"] C --> KEEP2["design choice"] style PY fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style G fill:#fff9c4,stroke:#f57f17,stroke-width:3px style T fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style C fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style FIX fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style KEEP fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px style KEEP2 fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px
Cost of the division-based guards (% of runtime) small 1/2, 1/3 add 19% sub 22% mul 20% div 22% large values add 7% sub 9% mul 11% div 6%
graph TD OP["operator + - * /"] OP --> CHK["_mul_overflows(a, b)?"] CHK -->|"no"| FAST["exact integer path"] CHK -->|"yes"| SLOW["double fallback"] FAST --> RES["Fraction(n, d)"] SLOW --> RES style OP fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style CHK fill:#fff9c4,stroke:#f57f17,stroke-width:3px style FAST fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style SLOW fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style RES fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px
graph LR ADD["operator+"] ADD --> A1["gcd(b, d)"] A1 --> A2["ctor normalize\ngcd(n, d)"] A2 --> A3["redundant\nnormalize"] style ADD fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style A1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style A2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style A3 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px
graph TD Q["Where is the time?"] Q --> G["gcd count\n+ divisions"] Q --> B["branch overhead"] G --> F["fewer gcds\ncheaper overflow check"] B --> F style Q fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style G fill:#fff9c4,stroke:#f57f17,stroke-width:3px style B fill:#fff9c4,stroke:#f57f17,stroke-width:3px style F fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px