Moving integer division to floating-point is trivial
-->
-->
Moving integer division to floating-point is trivial | Marc B. Reynolds
posts<br>compact by date<br>categories<br>tags
github<br>mastodon<br>twitter<br>rss
top
Moving integer division to floating-point is trivial
--->
Moving integer division to floating-point is trivial
August<br>10th,
2026
Integer division q=(x/y) and remainder (of Euclidean division) r=(x%y) hardware operations are very sad<br>on current hardware. Typically very long latency and poor throughput. In contrast floating-point division<br>is pretty happy: shorter latency, higher throughput and often more execution units to perform the<br>operation. So there are cases it could be interesting to move some integer div/mod operations to floating point.<br>But it’s PITA right? Actually I think it’s easy. The math is pretty straightforward so if I’ve made a mistake<br>I expect to find out rather soon.
My claim is: for two integers x & y (signed or unsigned) that fit in 53/24 bits for double/single<br>precision respectively, with both promoted to floating point then:
// floating-point:<br>// d is the same integer as integer divide x / y<br>// m is the same integer as integer remainder x % y<br>// (in standard rounding mode: round-to-nearest, ties to even)
d = trunc(x/y); // floor works for unsigned<br>m = -fma(d,y,-x); // fma required
// NOTE: if only want 'd' and it's being converted to an<br>// integer then the truncate or floor operation is<br>// free in the float to integer conversion.
in standard rounding mode.
From here on I will only consider unsigned integers since it’s the harder case (signed have a smaller max magnitude) but recall<br>that floating point effective stores a signed magnitude quantity.
Some practical points:
this works by setting rounding-mode to TOWARD_ZERO and then back once done but hitting control words is often very expensive
some hardware have division opcodes that allow choosing the rounding mode on some operations like division.<br>Example AVX-512 has instrinsic: _mm_div_round_sd with example listed latency of 14 cycles.
converting int to float and back has a cost. (NOTE: the majority of x64 CPUs don’t have an op for unsigned to float and<br>back. So this is an implemenation concern.)
obviously the floating point precision $p$ places the upper bound on integer width. If this works for $p$ then all smaller<br>widths trivally work. Examples 32-bit integers in doubles ($p=53$) and 16-bit integers in singles ($p=24$). In this case it’s<br>possible that there’s some “trickery” to side-step the int to float.
obviously the most promising case is for working in SIMD to amortize overhead.
for constant divisors your compiler should eliminate. for runtime known divisors that are going to be reused a small number of<br>times there’s software solutions like libdivide.
Note that there are proven methods (SEE: Formally verified 32- and 64-bit integer division using double-precision floating-point arithmetic)
The standard rounding mode is: round-to-nearest (ties to even) and the first important observation is the ties<br>part. A tie happens when the exact result of an operation is exactly at the midpoint between two<br>floating point numbers. Floating point division (in our case of base-2 and same working precision) has zero<br>midpoints (SEE: Midpoints and exact points of some algebraic functions in floating-point arithmetic section 6.1, corollary 1).<br>Therefore there’s never a tie and only round-to-nearest portion applies.
Let’s look an example using a 4-bit precision floating point format with all possible configurations where the exact<br>result is less than one but as close as possible to rounding up:
|GRS x = don't care<br>.1111|0xx .1111 (no rounding)<br>.1111|100 tie case is impossible<br>.1111|101 1.000 (round up)<br>.1111|11x 1.000 (round up)
Prior to rounding the hardware computes three extra digits: guard bit (G), round bit (R) and sticky bit (S) but since<br>the tie case is impossible we only need to know G. Therefore in any format where the exact result has $r$ bits for the<br>fractional part then for rounding to the next integer to occur requires the fractional part to have at least $r+1$ leading ones.<br>I’m claiming that this is impossible with legal inputs.
Since we only need to consider what happens with the fractional part let’s breakdown the exact result of $x/y$ into its integer $n$ and fractional parts:
\[\frac{x}{y} = n + \frac{a}{b}\]
(where $b=y$ just to be less of an eyesore) so obviously:
\[\frac{a}{b} \in \left[0,1\right)\]
Given a $p$ precision binary floating point format the division produces a $d$-bit integer leaving $r$-bits for the remainder:
\[\begin{align*}<br>d & = \left \lfloor \log_2 \left(n \right)+1 \right \rfloor \\<br>r & = p-d \\<br>\end{align*}\]
For a $r$-bit fractional part we can define the lower and upper bound on it’s range of value for $b$:
\[\begin{align*}<br>\func{b_l}{r} & = 2^r \\<br>\func{b_u}{r} & = 2^{r+1}-1<br>\end{align*}\]
At this point we’re pretty much done with $p$, $n$ and its derived $d$. We...