Once an epoch, every Solana validator computes how many lamports of inflation to mint, adds them to the chain’s capitalization, and folds the result into its bank hash. Today that calculation runs in floating point, with a powf in the middle of it.
SIMD-0607, written by engineers from both Anza and Firedancer, replaces every floating-point step with integer arithmetic specified down to the order of its divisions. Agave merged the implementation on 30 October behind a feature gate. We copied the specification’s Rust into a test harness to check its claims and to measure the problem it fixes. Two mathematically identical float formulas for the same reward disagreed by one lamport in 62 of 60,000 epochs. The integer path cannot disagree with itself.
Why one lamport is a consensus failure
Capitalization is part of the state every validator hashes. If two clients compute the same epoch’s reward and land one lamport apart, their capitalizations differ, their bank hashes differ, and the network has a disagreement no vote can fix. The proposal’s motivation is exactly that: these values “must be computed identically by all validator clients, without relying on IEEE-754 behavior or platform math-library functions such as powf.”
Basic float operations are deterministic. Exponentiation is not specified to the last bit, and the inflation rate is 0.08 × (1 − taper)^year. To see how much that matters, we computed the legacy epoch reward two ways that are equal on paper, (1 − taper).powf(year) and exp(year × ln(1 − taper)), for 6,000 epochs in each of five slot-time regimes and both tapers:
About one epoch in a thousand, the truncation that turns a float reward into lamports lands on the other side of an integer. That is not a claim that Agave and Firedancer compute different rewards today. It is the reason neither should depend on how a math library rounds the last bit.
The integer schedule
Rates are integers over RATE_SCALE = 10^15, so 8% is 80_000_000_000_000. Decay factors are integers over 2^64, a finer scale because a factor very close to one is multiplied by itself across hundreds of millions of slots. The per-slot factor is derived, not tabulated: a 32-term series for the logarithm of the taper and a cubic approximation of the exponential, then a binary exponentiation whose order and flooring are fixed:
fn decay_per_slot(ns: u128, taper_num: u32, taper_den: u32) -> Option<u128> {
// -ln(1 - taper) = ln(taper_den / (taper_den - taper_num)), scaled by 2^64
let annual_exponent =
approximate_logarithm(taper_den, taper_den.checked_sub(taper_num)?)?;
let slot_exponent = annual_exponent.checked_mul(ns)? / YEAR_NS;
approximate_exponential(slot_exponent) // 2^64 * exp(-x), cubic
}
fn pow_decay_floor(mut base: u128, mut n: u64) -> u128 {
let mut result = DECAY_SCALE;
while n > 0 {
if n & 1 != 0 { result = mul_decay_floor(result, base); }
n >>= 1;
if n > 0 { base = mul_decay_floor(base, base); }
}
result
}
// rate = max(TERMINAL_RATE, floor(anchor_rate * decay / 2^64))
// annual = floor(capitalization * rate / 10^15)
// reward = floor(annual * slots_in_epoch * ns_per_slot / YEAR_NS)Every step floors, every multiplication is checked, and an overflow is an error the validator must turn into a panic. The specification forbids wrapping, saturating or substituting a value, and it forbids falling back to floating point.
Its numbers, reproduced to the lamport
The proposal publishes how far the integer schedule drifts from the floating-point one: the largest difference in a single epoch’s reward over 6,000 epochs, at a capitalization of 10¹⁸ lamports, across the 400, 350, 300, 250 and 200 ms slot regimes. Running its functions verbatim against its stated float baseline:
Both drift figures match exactly. So does the claim that 32 terms of the logarithm series are enough: for the 15% and 30% tapers, the 32-term sum and a 200-term sum are the same integer, to the last of 2⁶⁴ units. The drift is the price of the change, at most a few thousand lamports of an epoch’s reward. A relative difference below 7 × 10⁻¹¹ buys a reward every client computes identically.
Overflow, and why it panics
With 128-bit intermediates, the reward arithmetic stays valid up to a capitalization of about 18.4 trillion SOL, a thousand times beyond the 18.4 billion SOL a 64-bit lamport count can hold at all. The checks that can fail in practice are the invalid inputs: a zero slot duration, or a taper of 100% or more, return an error before any arithmetic.
The panic rule is deliberate. A validator that cannot compute the reward exactly as specified is safer stopped than running on a number the rest of the cluster did not compute. That is the same reasoning as the proposal itself, applied to its own failure path.
The activation order with SIMD-0550
SIMD-0550 doubles the yearly disinflation from 15% to 30%. Its original design kept issuance continuous by recomputing the stored initial rate at activation, initial = anchor / (1 − 0.30)^year, with powf. Our earlier post on SIMD-0550 described that design. An amendment merged on 29 October replaced it: SIMD-0607 must activate first or at the same boundary, the anchor is computed with the integer 15% schedule including its terminal clamp, and the 30% integer taper applies from the activation slot. Agave rekeyed the SIMD-0550 gate in the 4.4.0 release.
The anchor works: at zero elapsed slots the decay factor is exactly 2⁶⁴, so the rate does not move. The trap is for anyone reading the stored governor. After activation it reports initial = 0.08 and taper = 0.30, and the textbook formula built from those two numbers, which measures decay from genesis, lands on the 1.5% floor while the chain is minting at about 3%. Forward curves should start from getInflationRate, not from getInflationGovernor.
Where it stands
Agave’s implementation, #16355, merged on 30 October behind the remove_runtime_float_ops gate. Neither that gate nor SIMD-0550’s has been activated on mainnet. When SIMD-0607 does activate at the boundary into epoch E+1, rewards earned in epoch E are still paid with the legacy calculation, and the integer path starts with rewards earned in E+1.
What this does not show
We ran the specification’s code, not Agave’s implementation of it, and compared against the float baseline the specification states rather than against a running validator. The two float paths in section 01 are a test of sensitivity, not a measurement of any client. The year-6 activation in section 05 is illustrative, at constant 400 ms slots; the real anchor depends on the slot-time history up to the activation slot.
Integer Inflation: Questions People Actually Ask
Why does Solana want to remove floating point from inflation?
Inflation rewards are added to capitalization, which is part of the bank hash. If two validator clients compute an epoch’s reward one lamport apart, their bank hashes differ. Floating-point exponentiation depends on the math library, and mathematically identical float formulas can round to different lamport amounts. SIMD-0607 specifies the whole calculation in integers so every client gets the same number.
How large is the difference between the integer and floating-point schedules?
At most 5,923 lamports per epoch for the 15% taper and 1,061 for the 30% taper, over 6,000 epochs and five slot-time regimes at a capitalization of 10^18 lamports, a relative difference below 7.03 × 10^-11. We reproduced both figures exactly from the specification’s code.
What does SIMD-0607 have to do with SIMD-0550?
SIMD-0550 doubles the disinflation rate from 15% to 30% a year. Since an amendment merged on 29 October 2026, it must activate after or together with SIMD-0607: the anchor rate at activation is computed with the integer 15% schedule, and the 30% integer taper applies from there. The stored inflation governor keeps initial 0.08 and switches taper to 0.30.
Can I compute Solana’s inflation rate from getInflationGovernor?
Not after SIMD-0550 activates. The governor will report initial 0.08 and taper 0.30, but the rate decays from the activation anchor, not from genesis, so initial × (1 − taper)^years gives the wrong answer. Read getInflationRate, which the node computes.
Is SIMD-0607 active on mainnet?
Not as of 30 October 2026. Agave merged the implementation that day behind the remove_runtime_float_ops feature gate, which has not been activated on mainnet.
The one-line version
Floating point lets two correct implementations disagree by a lamport about once in a thousand epochs; SIMD-0607 trades a few thousand lamports of drift per epoch for a reward every client computes identically, and SIMD-0550 now waits for it.
The faster disinflation itself is in Solana’s inflation will decay twice as fast, how those rewards reach stake accounts is in when staking rewards arrive, and why two independent clients matter at all is in which client built each block.
Sources: SIMD-0607 as amended at commit 2934dbeb; the SIMD-0550 amendment; Agave #16355 (commit 9bcf93e368) and #16499. Measurements are from the specification’s functions run in our own harness.