2026-07-07
IEEE 754 defines four rounding modes: round-to-nearest-even (the default), round-toward-zero (truncation), round-toward-positive-infinity, and round-toward-negative-infinity. Your CPU stores the current mode in a control register — MXCSR on x86, FPCR on ARM, fcsr on RISC-V. Every FP arithmetic instruction implicitly reads this register to decide how to round its result. That sounds cheap. It isn't.
The problem is that the rounding mode is global state shared by every in-flight FP instruction. When you write to MXCSR with an ldmxcsr or use fesetround(), the CPU can't just update the register and move on. Every FP instruction already dispatched into the out-of-order engine was speculatively assuming the old mode. The CPU has two choices: track the mode per-instruction (expensive silicon) or serialize.
Real CPUs pick serialize. On Intel Skylake, writing MXCSR triggers a partial pipeline flush: the front-end stalls, the scheduler drains all pending FP ops, and only then does the new mode take effect. Measured cost: roughly 100–300 cycles, comparable to a branch misprediction. ARM's FPCR write is similar. On some cores it's implemented as a full serializing instruction.
The classic trap: C99's lrint() respects the current rounding mode, but (long)x always truncates. Programmers who need truncation sometimes write:
fesetround(FE_TOWARDZERO);result = lrint(x);fesetround(FE_TONEAREST);That's two serializing writes per conversion — 400+ cycles of overhead to save a single instruction. The fix is cvttsd2si (SSE2's truncating convert), which encodes the rounding mode in the opcode itself and needs no MXCSR change. AVX-512 generalized this with the {rn-sae}, {rd-sae}, {ru-sae}, {rz-sae} embedded rounding hints on any FP instruction — the rounding mode becomes part of the instruction encoding, no register write required.
Rule of thumb: if you touch the rounding mode inside a hot loop, you've built a pipeline flush generator. Set it once at program start, or use instructions with embedded rounding. Interval arithmetic libraries (CRlibm, MPFR) are notorious for this — naive implementations switch modes between every operation and run 50× slower than the FLOP count suggests.
Some CPUs (Apple's M-series, recent AMD) implement partial mode renaming: they checkpoint the rounding mode alongside register-rename state so writes don't require a full drain. Even so, the cost is nonzero, and the compiler generally assumes it's expensive and hoists mode changes out of loops.
