When dividing a numerator n by a divisor d, most programming languages round “down”. It means that 1/2 is 0. Mathematicians will insist that 1/2 and claim that you really are computing floor(1/2). But let me think like a programmer. So 3/2 is 1.
If you always want to round up, you can instead compute (n + d – 1)/d. If I want to apply it to n is 3 and d is 2, I do (3 + 2 – 1) /2 = 2.
We sometimes want to round the value to the nearest integer. So 1.4 becomes 1, 1.6 becomes 2. But what if you have 1.5 (the result of 3/2)? You can either round up or round down.
- You can round “up” when hitting the midpoint with (n + d/2)/d.
- You can round “down” when hitting the midpoint with (n – d/2)/d.
But there is a third common way to round. You want to round to even. It means that when you hit the midpoint, you round to the nearest even integer. So 1.5 becomes 2, 2.5 becomes 2, 3.5 becomes 4 and so forth. I am sure that it has been worked out before, but I could not find an example so I rolled my own:
off = (n + d / 2) roundup = off / d; ismultiple = ((off % d) == 0); iseven = (d & 1) == 0; return (ismultiple && iseven) ? roundup - (roundup & 1) : roundup;
Though there is a comparison and what appears like a branch, I expect most compilers to produce essentially branchless code. The result should be about five instructions on most hardware. I expect that it will be faster than converting the result to a floating-point number, rounding it up and converting the resulting floating-point number back.
I have posted working source code.
Why does it work?
Firstly, you never have to worry about hitting the midpoint if the divisor is odd. The remainder of a division by an odd number cannot be equal to d/2. Thus, starting from the round-up approach (n + d/2)/d we only need to correct the result. When n is equal to d + d/2, we need to round up, when it is equal to 2 d + d/2 we need to round down and so forth. So we only need to remove 1 from (n + d/2)/d when n is equal to 2kd+d/2 for some integer k. But when n is equal to 2kd+d/2, I have that n + d/2 is equal to (2k+1)d. That is, the quotient is odd.
Even better: You can make it explicitly branchless (credit: Falk Hüffner):
off = (n + d / 2) roundup = off / d; ismultiple = ((off % d) == 0); return (d | (roundup ^ ismultiple)) & roundup
Nitpicking: You may be concerned with overflows. Indeed, if both n and d are large, it is possible for n+d/2 to exceed to allowable range. However, the above functions are used in the Linux kernel. And you can make them more robust at the expensive of a little bit more work.
Credit: This blog post was motivated by an email exchange with Colin Bartlett,
2 thoughts on “Rounding integers to even, efficiently”
Depending on which instructions are available, you might get slightly better code by replacing the last two lines with
return (d | (roundup ^ ismultiple)) & roundup;
Wow. I doubt it will be faster, at least on recent x64 processors, but your approach is nicer in that it is explicitly branchless. I like it a lot!!!
You may subscribe to this blog by email.