Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Mathematical Honesty

The Floating Point Lie

One of the most shocking things new programmers discover:

# Python (same in JavaScript, Java, C++, C#, Ruby, etc.)
result = 0.1 + 0.2
print(result)  # 0.30000000000000004 ❌

Teachers say “don’t worry about it” or “you’ll get used to it.” But why should we?

Why Does This Happen?

Most languages use IEEE 754 floating-point arithmetic, which represents numbers in binary:

  • 0.1 in binary is 0.0001100110011001100... (infinite repeating)
  • 0.2 in binary is 0.001100110011001100... (infinite repeating)
  • Binary can’t represent these exactly, so they’re rounded
  • Adding rounded numbers gives wrong answers

This was a reasonable choice in 1970 when:

  • Memory was expensive
  • CPUs had hardware floating-point units
  • Alternative precision libraries were slow

But it’s 2025 now. We can do better.

The SFX Solution

SFX uses arbitrary precision arithmetic by default:

Story:
    Result is 0.1 + 0.2
    Print Result  # 0.3 ✓

    # Financial calculations
    Price is 99.99
    Tax is Price * 0.08
    Total is Price + Tax
    Print Total  # 107.9892 (exact!)

    # Very large numbers
    Big is 123456789123456789123456789
    Bigger is Big * 2
    Print Bigger  # 246913578246913578246913578

How It Works

SFX uses the BigDecimal type for the default Number type:

  • Numbers are stored as integers with a scale factor
  • 0.1 is stored as 1 with scale -1 (meaning 1 × 10^-1)
  • Operations maintain exact precision
  • No rounding errors accumulate

Example

Story:
    # Exact decimal representation
    A is 0.1        # Stored exactly
    B is 0.2        # Stored exactly
    C is A + B      # 0.3 exactly

    # This always works:
    If C = 0.3:
        Print "Math works!"  # Always prints ✓

When You Need Speed: FastNumber

For performance-critical code (games, physics, graphics), use FastNumber:

Story:
    # IEEE 754 float for speed
    Speed is FastNumber(299792458.0)
    Time is FastNumber(2.5)
    Distance is Speed * Time

    # About 10x faster than Number
    # But you get float errors again:
    Test is FastNumber(0.1) + FastNumber(0.2)
    # 0.30000000000000004

When to use FastNumber:

  • Physics simulations
  • Graphics/game engines
  • High-frequency calculations
  • When performance > precision

When to use Number (default):

  • Financial calculations
  • User-facing math
  • Counting/indexing
  • When correctness matters
  • Learning/teaching programming

Real-World Impact

Financial Software

Concept: Invoice
    Subtotal, TaxRate, Tax, Total

    When Subtotal changes:
        Set This.Tax to This.Subtotal * This.TaxRate
        Set This.Total to This.Subtotal + This.Tax

Story:
    Create Invoice Called Bill
    Set Bill.Subtotal to 99.99
    Set Bill.TaxRate to 0.08

    # Exact: $107.9892
    # Float: $107.98919999999999 (wrong!)
    Print Bill.Total

With floats, multiply this error by millions of transactions, and you’re off by thousands of dollars.

Scientific Computing

Story:
    # Sum a series: 1 + 1/2 + 1/3 + 1/4 + ...
    Sum is 0
    Repeat 10000 times with I:
        Sum is Sum + (1 / I)

    # SFX: Exact answer
    # Float: Accumulated rounding errors
    Print Sum

Teaching Math

When teaching programming to kids, having 0.1 + 0.2 = 0.3 work correctly means:

  • Less confusion
  • Math matches what they learned in school
  • One less “computers are weird” moment
  • Focus on logic, not float quirks

Performance Comparison

OperationNumber (exact)FastNumber (float)
Addition100%~1000% (10x faster)
Multiplication100%~1000%
Division100%~1000%

With JIT compilation, Number operations get optimized, narrowing the gap.

Examples

Banking

Concept: BankAccount
    Balance

    To Deposit with Amount:
        Set This.Balance to This.Balance + Amount

    To Withdraw with Amount:
        Set This.Balance to This.Balance - Amount

Story:
    Create BankAccount Called Checking
    Set Checking.Balance to 0

    # Deposit cents
    Checking.Deposit with 0.01
    Repeat 100 times:
        Checking.Deposit with 0.01

    # Balance is exactly $1.01
    Print Checking.Balance  # 1.01 ✓

With floats, you’d get 1.0100000000000007 or similar.

E-commerce

Concept: ShoppingCart
    Items, Subtotal, Discount, Tax, Total

    To CalculateTotal:
        ItemsSum is 0
        For each Item in This.Items:
            ItemsSum is ItemsSum + Item["price"]

        Set This.Subtotal to ItemsSum
        DiscountAmount is This.Subtotal * This.Discount
        TaxableAmount is This.Subtotal - DiscountAmount
        TaxAmount is TaxableAmount * This.Tax
        Set This.Total to TaxableAmount + TaxAmount

Story:
    Create ShoppingCart Called Cart
    Set Cart.Items to [
        { name: "Item 1", price: 19.99 },
        { name: "Item 2", price: 29.99 },
        { name: "Item 3", price: 9.99 }
    ]
    Set Cart.Discount to 0.10  # 10% off
    Set Cart.Tax to 0.08       # 8% tax

    Cart.CalculateTotal
    Print "Total: $" + Cart.Total  # Exact to the cent!

Best Practices

1. Use Number by Default

# Good - exact
Price is 19.99
Tax is Price * 0.08

2. Use FastNumber for Performance

# Physics engine
Concept: Particle
    X, Y, VX, VY

    To Update with DT:
        # Convert to FastNumber for speed
        FDT is FastNumber(DT)
        Set This.X to This.X + (This.VX * FDT)
        Set This.Y to This.Y + (This.VY * FDT)

3. Don’t Mix Types Unnecessarily

# Avoid mixing Number and FastNumber
A is 10
B is FastNumber(20.0)
C is A + B  # Works, but converts to FastNumber (loses precision)

4. Round for Display

# For user display, round to appropriate precision
Price is 19.99
Tax is Price * 0.085  # 1.69915
Total is Price + Tax  # 21.68915

# Display as currency (2 decimal places)
DisplayTotal is Math.Round(Total * 100) / 100
Print "$" + DisplayTotal  # $21.69

Summary

SFX’s mathematical honesty means:

Correct by default - Math works like school taught you ✓ No surprises - 0.1 + 0.2 = 0.3 ✓ Financial accuracy - Pennies don’t go missing ✓ Easier to learn - No float quirks to explain ✓ FastNumber available - Performance when you need it

Programming languages should make correctness easy and performance opt-in, not the other way around.


Next: 1-Based Indexing - Why lists start at 1