Files
2026-07-13 12:09:03 +08:00

720 lines
23 KiB
Python

import math
import struct
import random
def softmax_naive(logits):
exps = [math.exp(z) for z in logits]
total = sum(exps)
return [e / total for e in exps]
def softmax_stable(logits):
max_logit = max(logits)
exps = [math.exp(z - max_logit) for z in logits]
total = sum(exps)
return [e / total for e in exps]
def logsumexp_naive(values):
return math.log(sum(math.exp(v) for v in values))
def logsumexp_stable(values):
c = max(values)
return c + math.log(sum(math.exp(v - c) for v in values))
def log_softmax_stable(logits):
c = max(logits)
lse = c + math.log(sum(math.exp(z - c) for z in logits))
return [z - lse for z in logits]
def cross_entropy_naive(true_class, logits):
probs = softmax_naive(logits)
return -math.log(probs[true_class])
def cross_entropy_stable(true_class, logits):
log_probs = log_softmax_stable(logits)
return -log_probs[true_class]
def sigmoid_naive(x):
return 1.0 / (1.0 + math.exp(-x))
def sigmoid_stable(x):
if x >= 0:
z = math.exp(-x)
return 1.0 / (1.0 + z)
else:
z = math.exp(x)
return z / (1.0 + z)
def binary_cross_entropy_naive(y_true, y_pred):
return -(y_true * math.log(y_pred) + (1 - y_true) * math.log(1 - y_pred))
def binary_cross_entropy_stable(y_true, logit):
max_val = max(0.0, -logit)
return max_val + math.log(math.exp(-max_val) + math.exp(-logit - max_val)) - y_true * logit
def numerical_gradient(f, x, h=1e-5):
grad = []
for i in range(len(x)):
x_plus = x[:]
x_minus = x[:]
x_plus[i] += h
x_minus[i] -= h
grad.append((f(x_plus) - f(x_minus)) / (2 * h))
return grad
def check_gradient(analytical, numerical, tolerance=1e-5):
all_ok = True
for i, (a, n) in enumerate(zip(analytical, numerical)):
denom = max(abs(a), abs(n), 1e-8)
rel_error = abs(a - n) / denom
status = "OK" if rel_error < tolerance else "FAIL"
if status == "FAIL":
all_ok = False
print(f" param {i}: analytical={a:.8f} numerical={n:.8f} "
f"rel_error={rel_error:.2e} [{status}]")
return all_ok
def clip_by_value(gradients, max_val):
return [max(-max_val, min(max_val, g)) for g in gradients]
def clip_by_norm(gradients, max_norm):
total_norm = math.sqrt(sum(g ** 2 for g in gradients))
if total_norm > max_norm:
scale = max_norm / total_norm
return [g * scale for g in gradients]
return list(gradients)
def check_tensor(name, values):
has_nan = any(math.isnan(v) for v in values)
has_inf = any(math.isinf(v) for v in values)
n_nan = sum(1 for v in values if math.isnan(v))
n_inf = sum(1 for v in values if math.isinf(v))
if has_nan or has_inf:
print(f" WARNING {name}: {n_nan} NaN, {n_inf} Inf out of {len(values)} values")
return False
print(f" OK {name}: all {len(values)} values finite")
return True
def simulate_bfloat16(x):
packed = struct.pack('f', x)
as_int = int.from_bytes(packed, 'little')
truncated = as_int & 0xFFFF0000
repacked = truncated.to_bytes(4, 'little')
return struct.unpack('f', repacked)[0]
def simulate_float16(x):
try:
packed = struct.pack('e', x)
return struct.unpack('e', packed)[0]
except (OverflowError, struct.error):
return float('inf') if x > 0 else float('-inf')
def kahan_sum(values):
total = 0.0
compensation = 0.0
for v in values:
y = v - compensation
t = total + y
compensation = (t - total) - y
total = t
return total
def welford_variance(values):
n = 0
mean = 0.0
m2 = 0.0
for x in values:
n += 1
delta = x - mean
mean += delta / n
delta2 = x - mean
m2 += delta * delta2
if n < 2:
return 0.0
return m2 / n
def variance_naive(values):
n = len(values)
mean_x = sum(values) / n
mean_x2 = sum(v ** 2 for v in values) / n
return mean_x2 - mean_x ** 2
def layer_norm(values, epsilon=1e-5, gamma=1.0, beta=0.0):
n = len(values)
mean = sum(values) / n
var = sum((v - mean) ** 2 for v in values) / n
std = math.sqrt(var + epsilon)
return [(v - mean) / std * gamma + beta for v in values]
def demo_float_precision():
print("=" * 60)
print("DEMO 1: Floating Point Precision Limits")
print("=" * 60)
print(f"\n 0.1 + 0.2 = {0.1 + 0.2}")
print(f" 0.1 + 0.2 == 0.3? {0.1 + 0.2 == 0.3}")
print(f" Difference from 0.3: {(0.1 + 0.2) - 0.3:.2e}")
print(f" math.isclose(0.1 + 0.2, 0.3): {math.isclose(0.1 + 0.2, 0.3)}")
print(f"\n Float32 max: ~{3.4028235e+38:.2e}")
print(f" Float32 min positive (normal): ~{1.175e-38:.2e}")
print(f" Float32 epsilon: ~{1.1920929e-07:.2e}")
print(f"\n 1.0 + 1e-7 == 1.0? {1.0 + 1e-7 == 1.0}")
print(f" 1.0 + 1e-8 == 1.0? {1.0 + 1e-8 == 1.0}")
print(f" (These are float64 in Python. In float32, epsilon is ~1.19e-7)")
total_naive = 0.0
for _ in range(1_000_000):
total_naive += 1e-7
total_kahan = kahan_sum([1e-7] * 1_000_000)
true_value = 1e-7 * 1_000_000
print(f"\n Summing 1e-7 one million times:")
print(f" True value: {true_value}")
print(f" Naive sum: {total_naive:.10f} (error: {abs(total_naive - true_value):.2e})")
print(f" Kahan sum: {total_kahan:.10f} (error: {abs(total_kahan - true_value):.2e})")
print()
def demo_catastrophic_cancellation():
print("=" * 60)
print("DEMO 2: Catastrophic Cancellation")
print("=" * 60)
data = [1_000_000.0, 1_000_001.0, 1_000_002.0]
true_var = 2.0 / 3.0
var_naive = variance_naive(data)
var_welford = welford_variance(data)
print(f"\n Data: {data}")
print(f" True variance: {true_var:.10f}")
print(f" Naive (E[x^2] - E[x]^2): {var_naive:.10f}")
print(f" Welford (online): {var_welford:.10f}")
print(f" Naive error: {abs(var_naive - true_var):.2e}")
print(f" Welford error: {abs(var_welford - true_var):.2e}")
a = 1.0000001
b = 1.0000000
true_diff = 1e-7
computed_diff = a - b
rel_error = abs(computed_diff - true_diff) / true_diff * 100
print(f"\n Subtracting nearly equal numbers:")
print(f" a = {a}")
print(f" b = {b}")
print(f" True a - b = {true_diff}")
print(f" Computed: {computed_diff}")
print(f" Relative error: {rel_error:.1f}%")
print()
def demo_overflow_underflow():
print("=" * 60)
print("DEMO 3: Overflow and Underflow in exp() and log()")
print("=" * 60)
print("\n exp() overflow boundary (float64 in Python):")
for x in [700, 709, 709.78, 710]:
try:
result = math.exp(x)
print(f" exp({x}) = {result:.4e}")
except OverflowError:
print(f" exp({x}) = OVERFLOW")
print("\n exp() underflow (results become 0.0):")
for x in [-700, -745, -746]:
result = math.exp(x)
print(f" exp({x}) = {result}")
print("\n log() edge cases:")
for x in [1.0, 1e-300, 1e-323, 0.0]:
try:
if x == 0.0:
print(f" log(0.0) = -inf (mathematically)")
result = math.log(1e-323)
print(f" log(1e-323) = {result:.2f} (closest we can get)")
else:
result = math.log(x)
print(f" log({x}) = {result:.4f}")
except ValueError:
print(f" log({x}) = DOMAIN ERROR")
print("\n Float16 overflow boundary:")
for val in [65000.0, 65504.0, 65520.0, 70000.0]:
f16 = simulate_float16(val)
print(f" float16({val}) = {f16}")
print()
def demo_softmax_stability():
print("=" * 60)
print("DEMO 4: Naive vs Stable Softmax")
print("=" * 60)
safe_logits = [2.0, 1.0, 0.1]
print(f"\n Safe logits: {safe_logits}")
naive_result = softmax_naive(safe_logits)
stable_result = softmax_stable(safe_logits)
print(f" Naive: {[f'{p:.6f}' for p in naive_result]}")
print(f" Stable: {[f'{p:.6f}' for p in stable_result]}")
print(f" Match: {all(abs(a - b) < 1e-10 for a, b in zip(naive_result, stable_result))}")
moderate_logits = [100.0, 101.0, 102.0]
print(f"\n Moderate logits: {moderate_logits}")
stable_result = softmax_stable(moderate_logits)
print(f" Stable: {[f'{p:.6f}' for p in stable_result]}")
try:
naive_result = softmax_naive(moderate_logits)
print(f" Naive: {[f'{p:.6f}' for p in naive_result]}")
except OverflowError:
print(" Naive: OVERFLOW (exp(100) too large)")
extreme_logits = [1000.0, 1001.0, 1002.0]
print(f"\n Extreme logits: {extreme_logits}")
stable_result = softmax_stable(extreme_logits)
print(f" Stable: {[f'{p:.6f}' for p in stable_result]}")
print(" Naive: would be [nan, nan, nan] or OVERFLOW")
negative_logits = [-1000.0, -999.0, -998.0]
print(f"\n Very negative logits: {negative_logits}")
stable_result = softmax_stable(negative_logits)
print(f" Stable: {[f'{p:.6f}' for p in stable_result]}")
print(" Naive: would be [0/0 = nan] (all exp() underflow to 0)")
print()
def demo_logsumexp():
print("=" * 60)
print("DEMO 5: Log-Sum-Exp Trick")
print("=" * 60)
safe = [1.0, 2.0, 3.0]
print(f"\n Safe values: {safe}")
print(f" Naive: {logsumexp_naive(safe):.10f}")
print(f" Stable: {logsumexp_stable(safe):.10f}")
large = [500.0, 501.0, 502.0]
print(f"\n Large values: {large}")
print(f" Stable: {logsumexp_stable(large):.10f}")
try:
naive = logsumexp_naive(large)
print(f" Naive: {naive}")
except OverflowError:
print(" Naive: OVERFLOW")
very_negative = [-1000.0, -999.0, -998.0]
print(f"\n Very negative values: {very_negative}")
print(f" Stable: {logsumexp_stable(very_negative):.10f}")
equal = [5.0, 5.0, 5.0]
print(f"\n Equal values: {equal}")
expected = 5.0 + math.log(3.0)
print(f" Stable: {logsumexp_stable(equal):.10f}")
print(f" Expected: {expected:.10f} (= 5.0 + ln(3))")
one_dominant = [100.0, 1.0, 1.0]
print(f"\n One dominant value: {one_dominant}")
print(f" Stable: {logsumexp_stable(one_dominant):.10f}")
print(f" ~100.0 (dominated by exp(100))")
print()
def demo_cross_entropy():
print("=" * 60)
print("DEMO 6: Stable Cross-Entropy Loss")
print("=" * 60)
logits = [2.0, 5.0, 1.0]
true_class = 1
print(f"\n Logits: {logits}, true class: {true_class}")
ce_naive = cross_entropy_naive(true_class, logits)
ce_stable = cross_entropy_stable(true_class, logits)
print(f" Naive: {ce_naive:.10f}")
print(f" Stable: {ce_stable:.10f}")
print(f" Match: {abs(ce_naive - ce_stable) < 1e-10}")
large_logits = [100.0, 105.0, 99.0]
true_class = 1
print(f"\n Large logits: {large_logits}, true class: {true_class}")
ce_stable = cross_entropy_stable(true_class, large_logits)
print(f" Stable: {ce_stable:.10f}")
try:
ce_naive = cross_entropy_naive(true_class, large_logits)
print(f" Naive: {ce_naive:.10f}")
except (OverflowError, ValueError):
print(" Naive: OVERFLOW or NaN")
confident_logits = [0.0, 0.0, 50.0]
true_class = 2
ce = cross_entropy_stable(true_class, confident_logits)
print(f"\n Very confident prediction:")
print(f" Logits: {confident_logits}, true class: {true_class}")
print(f" Loss: {ce:.10f} (near zero, model is correct and confident)")
wrong_logits = [0.0, 0.0, 50.0]
true_class = 0
ce = cross_entropy_stable(true_class, wrong_logits)
print(f"\n Very wrong prediction:")
print(f" Logits: {wrong_logits}, true class: {true_class}")
print(f" Loss: {ce:.4f} (very large, model is confident but wrong)")
print()
def demo_sigmoid_stability():
print("=" * 60)
print("DEMO 7: Stable Sigmoid")
print("=" * 60)
test_values = [0.0, 1.0, -1.0, 10.0, -10.0, 100.0, -100.0, 500.0, -500.0, 710.0, -710.0]
print(f"\n {'x':>8s} {'naive':>14s} {'stable':>14s}")
print(f" {'-'*8} {'-'*14} {'-'*14}")
for x in test_values:
try:
naive = sigmoid_naive(x)
naive_str = f"{naive:.10f}"
except OverflowError:
naive_str = "OVERFLOW"
stable = sigmoid_stable(x)
print(f" {x:>8.1f} {naive_str:>14s} {stable:.10f}")
print()
def demo_gradient_checking():
print("=" * 60)
print("DEMO 8: Gradient Checking")
print("=" * 60)
print("\n Test 1: f(x,y) = x^2 + 3xy + y^3")
def f1(params):
x, y = params
return x ** 2 + 3 * x * y + y ** 3
def f1_grad(params):
x, y = params
return [2 * x + 3 * y, 3 * x + 3 * y ** 2]
point = [2.0, 1.0]
analytical = f1_grad(point)
numerical = numerical_gradient(f1, point)
print(f" Point: {point}")
check_gradient(analytical, numerical)
print("\n Test 2: f(x) = softmax cross-entropy")
def f2(logits):
return cross_entropy_stable(0, logits)
logits = [2.0, 1.0, 0.5]
probs = softmax_stable(logits)
analytical_ce = [probs[i] - (1.0 if i == 0 else 0.0) for i in range(len(logits))]
numerical_ce = numerical_gradient(f2, logits)
print(f" Logits: {logits}")
check_gradient(analytical_ce, numerical_ce)
print("\n Test 3: Deliberately wrong gradient (should FAIL)")
def f3(params):
x, y = params
return x ** 2 + y ** 2
wrong_grad = [1.0, 1.0]
numerical_f3 = numerical_gradient(f3, [3.0, 4.0])
print(f" Wrong analytical: {wrong_grad}")
print(f" Correct numerical: {[f'{g:.4f}' for g in numerical_f3]}")
check_gradient(wrong_grad, numerical_f3)
print()
def demo_nan_inf():
print("=" * 60)
print("DEMO 9: NaN and Inf Detection and Propagation")
print("=" * 60)
print("\n How inf appears:")
print(f" 1.0 / 0.0 = {float('inf')}")
print(f" exp(710) = overflow -> inf")
print(f" 1e308 * 10 = {1e308 * 10}")
print("\n How nan appears:")
print(f" 0.0 / 0.0 = {float('nan')}")
print(f" inf - inf = {float('inf') - float('inf')}")
print(f" inf * 0 = {float('inf') * 0}")
print(f" nan + 1 = {float('nan') + 1}")
print(f" nan == nan = {float('nan') == float('nan')}")
print(f" nan < 0 = {float('nan') < 0}")
print(f" nan > 0 = {float('nan') > 0}")
print("\n NaN propagation (one nan ruins everything):")
values = [1.0, 2.0, float('nan'), 4.0, 5.0]
print(f" values = {values}")
print(f" sum = {sum(values)}")
print(f" max = nan (comparison with nan is always False)")
print(f" mean = {sum(values) / len(values)}")
print("\n Tensor health checks:")
check_tensor("weights", [0.1, -0.3, 0.5, 0.2])
check_tensor("logits_bad", [1.0, float('inf'), -2.0])
check_tensor("grads_bad", [0.01, float('nan'), -0.03])
check_tensor("activations", [0.0, 0.5, 1.0, 0.3])
print()
def demo_gradient_clipping():
print("=" * 60)
print("DEMO 10: Gradient Clipping")
print("=" * 60)
grads = [10.0, 20.0, 30.0]
norm = math.sqrt(sum(g ** 2 for g in grads))
print(f"\n Gradients: {grads}")
print(f" Norm: {norm:.4f}")
clipped_val = clip_by_value(grads, max_val=15.0)
clipped_norm = clip_by_norm(grads, max_norm=5.0)
print(f"\n Clip by value (max=15.0): {clipped_val}")
print(f" Clip by value changes direction: "
f"{[g/grads[0] for g in grads]} vs {[g/clipped_val[0] for g in clipped_val]}")
print(f"\n Clip by norm (max=5.0): {[f'{g:.4f}' for g in clipped_norm]}")
clipped_norm_val = math.sqrt(sum(g ** 2 for g in clipped_norm))
print(f" Clipped norm: {clipped_norm_val:.4f}")
print(f" Direction preserved: "
f"{[round(g/grads[0], 4) for g in grads]} == "
f"{[round(g/clipped_norm[0], 4) for g in clipped_norm]}")
print("\n Gradient explosion simulation:")
grad_val = 1.0
max_norm = 1.0
for step in range(8):
grad_val *= 3.5
clipped = clip_by_norm([grad_val], max_norm)[0]
print(f" Step {step}: raw_grad={grad_val:>12.2f} clipped={clipped:>8.4f}")
print()
def demo_mixed_precision():
print("=" * 60)
print("DEMO 11: Mixed Precision and Loss Scaling")
print("=" * 60)
print("\n bfloat16 vs float16 precision:")
test_values = [1.0, 0.1, 3.14159, 100.0, 65504.0, 65536.0, 100000.0]
print(f" {'value':>12s} {'float16':>12s} {'bfloat16':>12s}")
print(f" {'-'*12} {'-'*12} {'-'*12}")
for v in test_values:
f16 = simulate_float16(v)
bf16 = simulate_bfloat16(v)
f16_str = f"{f16:.4f}" if not math.isinf(f16) else "inf"
bf16_str = f"{bf16:.4f}" if not math.isinf(bf16) else "inf"
print(f" {v:>12.4f} {f16_str:>12s} {bf16_str:>12s}")
print("\n Loss scaling simulation:")
random.seed(42)
n_grads = 1000
tiny_grads = [random.uniform(1e-9, 1e-5) for _ in range(n_grads)]
zeros_without_scaling = sum(1 for g in tiny_grads if simulate_float16(g) == 0.0)
scale = 1024.0
scaled_grads = [g * scale for g in tiny_grads]
zeros_with_scaling = sum(1 for g in scaled_grads if simulate_float16(g) == 0.0)
scaled_back = [simulate_float16(g * scale) / scale for g in tiny_grads]
zeros_after_roundtrip = sum(1 for g in scaled_back if g == 0.0)
print(f" {n_grads} gradients in range [1e-9, 1e-5]")
print(f" Zeros without scaling: {zeros_without_scaling}/{n_grads} "
f"({zeros_without_scaling/n_grads*100:.1f}%)")
print(f" Zeros with scaling (x{scale:.0f}): {zeros_with_scaling}/{n_grads} "
f"({zeros_with_scaling/n_grads*100:.1f}%)")
print(f" Zeros after scale+convert+unscale: {zeros_after_roundtrip}/{n_grads} "
f"({zeros_after_roundtrip/n_grads*100:.1f}%)")
print("\n Dynamic loss scaling simulation:")
scale_factor = 65536.0
no_overflow_steps = 0
growth_interval = 100
print(f" {'step':>6s} {'scale':>12s} {'event':s}")
for step in range(500):
grad = random.gauss(0, 1)
scaled = grad * scale_factor
if math.isinf(simulate_float16(scaled)):
scale_factor /= 2
no_overflow_steps = 0
if step < 20 or step % 100 == 0:
print(f" {step:>6d} {scale_factor:>12.0f} overflow -> halved")
else:
no_overflow_steps += 1
if no_overflow_steps >= growth_interval:
scale_factor *= 2
no_overflow_steps = 0
if step < 100 or step % 100 == 0:
print(f" {step:>6d} {scale_factor:>12.0f} stable -> doubled")
print(f" Final scale factor: {scale_factor:.0f}")
print()
def demo_layer_norm():
print("=" * 60)
print("DEMO 12: Normalization as Numerical Stabilizer")
print("=" * 60)
print("\n Without normalization (values grow through layers):")
values = [1.0, 0.5, -0.3, 0.8, -0.1]
for layer in range(10):
values = [max(0, v * 2.5 + 0.1) for v in values]
max_val = max(abs(v) for v in values)
if layer % 2 == 0:
print(f" Layer {layer:>2d}: max={max_val:>12.2f} values={[f'{v:.2f}' for v in values[:3]]}...")
print("\n With layer normalization (values stay bounded):")
values = [1.0, 0.5, -0.3, 0.8, -0.1]
for layer in range(10):
values = [max(0, v * 2.5 + 0.1) for v in values]
values = layer_norm(values)
max_val = max(abs(v) for v in values)
if layer % 2 == 0:
print(f" Layer {layer:>2d}: max={max_val:>6.4f} values={[f'{v:.4f}' for v in values[:3]]}...")
print()
def demo_common_bugs():
print("=" * 60)
print("DEMO 13: Common ML Numerical Bugs")
print("=" * 60)
print("\n Bug 1: log(0) from confident wrong prediction")
logits = [100.0, -100.0, -100.0]
probs = softmax_stable(logits)
print(f" Softmax: {[f'{p:.2e}' for p in probs]}")
print(f" If true class is 1: log({probs[1]:.2e}) = ", end="")
if probs[1] == 0.0:
print("log(0) = -inf (CRASH)")
else:
print(f"{math.log(probs[1]):.2f}")
print(f" Stable cross-entropy handles this: {cross_entropy_stable(1, logits):.4f}")
print("\n Bug 2: exp() overflow in naive softmax")
logits = [800.0, 801.0, 802.0]
try:
naive = softmax_naive(logits)
print(f" Naive softmax: {naive}")
except OverflowError:
print(" Naive softmax: OverflowError (exp(800) is too large)")
stable = softmax_stable(logits)
print(f" Stable softmax: {[f'{p:.6f}' for p in stable]}")
print("\n Bug 3: Variance underflow with large-mean data")
data = [1e8 + 1, 1e8 + 2, 1e8 + 3, 1e8 + 4, 1e8 + 5]
var_naive = variance_naive(data)
var_welford = welford_variance(data)
true_var = 2.0
print(f" Data: [{data[0]:.0f}, ..., {data[-1]:.0f}]")
print(f" True variance: {true_var}")
print(f" Naive: {var_naive:.6f} (error: {abs(var_naive - true_var):.2e})")
print(f" Welford: {var_welford:.6f} (error: {abs(var_welford - true_var):.2e})")
print("\n Bug 4: Float comparison in training loop")
loss = 0.0
for _ in range(10):
loss += 0.1
print(f" After 10 steps of loss += 0.1: loss = {loss}")
print(f" loss == 1.0? {loss == 1.0} (WRONG)")
print(f" math.isclose(loss, 1.0)? {math.isclose(loss, 1.0)} (CORRECT)")
print("\n Bug 5: NaN from 0/0 in normalization")
values = [5.0, 5.0, 5.0, 5.0]
mean = sum(values) / len(values)
var = sum((v - mean) ** 2 for v in values) / len(values)
print(f" Constant input: {values}")
print(f" Variance: {var}")
print(f" 1/sqrt(var) = 1/sqrt(0) = ", end="")
try:
result = 1.0 / math.sqrt(var)
print(f"{result}")
except ZeroDivisionError:
print("ZeroDivisionError")
safe = 1.0 / math.sqrt(var + 1e-5)
print(f" 1/sqrt(var + 1e-5) = {safe:.2f} (safe with epsilon)")
print()
def demo_format_comparison():
print("=" * 60)
print("DEMO 14: Float Format Comparison Summary")
print("=" * 60)
print(f"""
Format Bits Exp Mantissa ~Digits Max Value Best For
------- ---- --- -------- ------- ---------- --------
float64 64 11 52 15-16 1.8e308 CPU training, accumulation
float32 32 8 23 7-8 3.4e38 Default training
float16 16 5 10 3-4 65,504 Inference
bfloat16 16 8 7 2-3 3.4e38 GPU/TPU training
float8 8 4 3 1-2 240 Forward pass only (H100+)
""")
print(" Precision test (representing pi):")
pi = math.pi
f16_pi = simulate_float16(pi)
bf16_pi = simulate_bfloat16(pi)
print(f" float64: {pi}")
print(f" float16: {f16_pi} (error: {abs(f16_pi - pi):.6f})")
print(f" bfloat16: {bf16_pi} (error: {abs(bf16_pi - pi):.6f})")
print("\n Range test (large values):")
for val in [100.0, 1000.0, 10000.0, 65504.0, 100000.0]:
f16 = simulate_float16(val)
bf16 = simulate_bfloat16(val)
f16_ok = "ok" if not math.isinf(f16) else "INF"
bf16_ok = "ok" if not math.isinf(bf16) else "INF"
print(f" {val:>10.0f} float16={f16_ok:>4s} bfloat16={bf16_ok:>4s}")
print()
if __name__ == "__main__":
demo_float_precision()
demo_catastrophic_cancellation()
demo_overflow_underflow()
demo_softmax_stability()
demo_logsumexp()
demo_cross_entropy()
demo_sigmoid_stability()
demo_gradient_checking()
demo_nan_inf()
demo_gradient_clipping()
demo_mixed_precision()
demo_layer_norm()
demo_common_bugs()
demo_format_comparison()
print("All demos complete.")