Document: P3724R3
Author: Jan Schultke
Date: 2026-02-20
Audience: LEWG
C++ only gives you truncating / and a remainder whose sign follows the dividend. That combo is fine until you want ceiling buckets, Python-style floor division, or nearest-with-ties rules - then the Stack Overflow answers start lying, overflowing, or invoking UB on INT_MIN. Schultke proposes a full family of std::div_* and std::div_rem_* plus a dedicated std::mod for the divisor-sign remainder you want in real modular arithmetic.
R3 rebases the wording on N5032, expands the language survey (including Julia-style rounding modes), and spells out tie-breaking motivation. SG6 already voted out div_to_even style top-level modes and sent an R2-shaped paper toward LEWG. The painful bit the paper hammers home: for any rounding mode except toward zero, computing remainder = x - q*y naively can multiply back into overflow even when the division itself is defined.
Paper: P3724R3 · Authors: Jan Schultke · Target: LEWG · Date: 2026-02-20
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
The paper is worth reading for section 3.2 alone. Everyone "knows"
remainder = x - quotient * divisor. For truncating division that is the identity you expect. For ceiling-style quotients it is not - the workedINT_MAXexample is the kind of thing that passes code review because the intermediateqlooks obviously right.That is why killing off paired
div_rem_*would be malpractice. The wording even puts the note in Returns so people stop cargo-culting the subtraction.I duplicated the example on Godbolt with
-ftrapvonce and the compiler basically laughed at me. The scary part is how silent it is in normal builds.Rust
/on integers truncates toward zero, same trap as C++. At least the paper's language table owns that instead of pretending the problem is unique to C++. Different languages, identical footgun.From the paper, on the classic "ceil division" snippet:
That is the comment thread in a microcosm - dozens of upvotes, subtle sign bug, nobody updates the disclaimer for ten years.
Teaching lens: students hit
%and think they got mathematical modulo. Then they port Python mental models and wonder why(a / b) * b + (a % b)stopped being the identity they memorized. Having spelled-outdiv_to_neg_infandmodin the standard library is the kind of thing that stops a whole week of office hours.LEWG is going to look at the synopsis and twitch. That is a lot of identifiers. I get why a runtime
roundingparameter lost - the paper shows the switch exploding into totally different micro-kernels - but emotionally I still want one entry point and a short enum.The whole point is the mode is almost always compile-time fixed. You want
div_to_pos_inf(x, y)to inline into the happy path for "how many buckets" without feeding a dead enum through the frontend so the optimizer has to prove the switch collapses.Fair. I will still whine about autocomplete noise on principle.
std::modis the one that will confuse people who only read the name. The paper basically admitsmodis a loaded word, then argues it is still the least bad label because it matches Haskell and friends for "remainder when you floor toward negative infinity". Worth reading §4.2.4 before bikeshedding on Reddit.Process note: SG6 took a consensus poll to drop
div_to_even/div_to_oddbecause nobody could name a real workload. The author even argues the names were easy to confuse with tie-breaking variants. If you miss them, you are officially on the wrong side of a unanimous-ish room.The ISO/IEC 60559 alignment table is clever politics. You can point at the floating-point rounding attributes and say "we are not inventing a zoo, we are mirroring the zoo that already ate numerical analysis."
Embedded angle: a lot of firmware code never sees negative indices, so the scary examples look academic - until the day someone feeds a backwards iterator into your ring-buffer math and your hand-rolled ceil macro goes nonlinear. I will take boring library names over another copy of the three-branch hack from 2004.
div_resultwithoperator<=>is the quiet modernization win - comparable pairs without draggingdiv_tweirdness into generics. P3161 energy, except division-shaped.§4.6 punts SIMD overloads to a follow-up. Smart -
div_remreturning something comparable with masks opens a second paper's worth of design questions. Still, game-math Twitter is going to ask anyway.Boost.SafeNumerics solves a different problem (policy-checked arithmetic). Nice complement if you need both rounding discipline and "no surprises on overflow" at the type level. github.com/boostorg/safe_numerics
P0105 got huge and stalled. P1889 carried pieces until it did not. This paper is basically "what if we shipped the division slice without reuniting the entire Crowl agenda." Historical baggage is half the discussion.
"Not noexcept because it inherits division's UB contract" is the correct call. People will still file issues asking for
expectedoverloads. Prepare emotionally.R3 adds Julia rounding modes to the survey table and half of you will still comment "lol just use Python //" without reading the signed case. Sir this is a Wendy's with defined wrap semantics.
Reference implementation lives at github.com/Eisenwave/integer-division if you want to diff against your favorite macro soup.
I hated
div_to_pos_infuntil I tried explainingfloorvs toward negative infinity to a intern on a whiteboard. Self-documenting wins even if it is verbose.__cpp_lib_integer_divisionplaceholder in the draft wording - nice that they remembered the macro story alongside saturating arithmetic neighbors.great, another paper that will take 10 years to get through LEWG
Honestly
std::divfrom C89 is the awkward ancestor everyone pretends not to know at parties. These new names at least tell you which ring you are working in.P3724 on GitHub: wg21.link/P3724/github - issue bot already has the revision chain if you want the tick-tock.