reading the clock, the coin, and the dice

Time, Money & Entropy

Hidden inputs from the outside world — the system clock and floating-point money — make the same call return a different answer each time.

A function that consults wall-clock time or money in floats answers differently every call.

01in the wild

In the wild

Reading the Wall Clock

A function that calls the clock can never be replayed; its answer depends on when you ran it.

example.js
// IMPURE: result depends on the wall clock
function greeting() {
  return new Date().getHours() < 12 ? "Good morning" : "Good evening";
}

// PURE: inject the hour; same input, same output, every time
const greet = (hour) =>
  hour < 12 ? "Good morning" : "Good evening";
Pass the clock in as an argument. Now the function is deterministic and trivially testable.
// observed
impure: greeting() flips at noon, untestable
pure:   greet(9) is always 'Good morning'
example.py
from datetime import datetime, timezone

# IMPURE: uses 'now' and the machine's local timezone
def is_expired(token):
    return token.expires_at < datetime.now()   # naive + nondeterministic

# PURE: inject an explicit, timezone-aware 'now'
def is_expired(token, now):
    return token.expires_at < now

is_expired(tok, datetime.now(timezone.utc))
datetime.now() is both nondeterministic and timezone-naive. Injecting an aware 'now' fixes correctness and testability at once.
// observed
impure: result depends on server clock & locale
pure:   pass a fixed 'now' to test any moment

Money in Floating Point

Binary floats cannot represent most decimals, so currency math drifts by pennies.

example.js
// SMELL: dollars as floats
0.1 + 0.2;                 // 0.30000000000000004
(0.1 + 0.2).toFixed(2);    // "0.30"  -- hides, doesn't fix

// RIGHT: work in integer cents, format only at the edge
const cents = (a, b) => a + b;     // 10 + 20 -> 30
const fmt = (c) => `$${(c / 100).toFixed(2)}`;
Floats round in binary, so decimals accumulate error. Store money as integer cents and only divide when displaying.
// observed
float: 0.1 + 0.2 = 0.30000000000000004
cents: 10 + 20 = 30 -> "$0.30"
example.py
# SMELL: float money accrues rounding error
total = 0.1 + 0.2          # 0.30000000000000004

# RIGHT: Decimal keeps base-10 money exact
from decimal import Decimal
total = Decimal("0.1") + Decimal("0.2")   # Decimal('0.3')
Decimal does base-10 arithmetic, so currency values stay exact. Never let user-facing money live in a binary float.
// observed
float:   0.30000000000000004
Decimal: 0.3
02cross-pollination

Where this compounds

03weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.