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
Nondeterminism
- Predictable ID Collision Under Load × Race Conditions
- Temp-File Check-Then-Use Race × File & Network Access
- Pool-Exhaustion Heisenbug × Resource Contention
Runtime Errors
- NaN / Infinity Poisons a Consumer × Lack of Input Validation
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.