views:

637

answers:

8

Using integer math alone, I'd like to "safely" average two unsigned ints in C++.

What I mean by "safely" is avoiding overflows (and anything else that can be thought of).

For instance, averaging 200 and 5000 is easy:

unsigned int a = 200;
unsigned int b = 5000;
unsigned int average = (a + b) / 2; // Equals: 2600 as intended

But in the case of 4294967295 and 5000 then:

unsigned int a = 4294967295;
unsigned int b = 5000;
unsigned int average = (a + b) / 2; // Equals: 2499 instead of 2147486147

The best I've come up with is:

unsigned int a = 4294967295;
unsigned int b = 5000;
unsigned int average = (a / 2) + (b / 2); // Equals: 2147486147 as expected

Are there better ways?

+1  A: 

Use a 64-bit unsigned int as the placeholder for the sum, cast back to int after dividing by 2. Questionable whether this is 'better', but you certainly avoid the overflow issue with minimal effort.

Steve Townsend
I'm doing this on an embedded micro with no 64bit registers ?
Martin Beckett
@Martin - then you are out of luck with my suggestion, sorry
Steve Townsend
+15  A: 
unsigned int average = low + ((high - low) / 2);

EDIT

Here's a related article: http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html

Sheldon L. Cooper
i like this, but what if there's an error due to integer division?
ianmac45
Why would there be? You're never dividing by 0, which is the only integer division that'd produce an error.
cHao
This is the classic answer to this problem, especially when you already know which value is high and which is low - choosing a midpoint, for example.
Mark Ransom
ordering them would be too expensive
ruslik
@ruslik: unless you know the ordering *a priori*, as in the linked article (which is probably the single most common use case for integer averaging).
Stephen Canon
@Stephen not really. while technicaly it's a bug, it's almost impossible to actually happen in a binary search. I'm sure that in this particular case it was quite common for sum to overflow.
ruslik
@ruslik, not really. Maybe it was almost impossible ten years ago, but not nowadays.
Sheldon L. Cooper
@ArunSaha: wrong! the original problem was about overflow. in this case you allow `high - low` to be signed, so this can easily overlow in the same way as in the original problem. you can avoid it only by considering this difference unsigned, so you have to know which one is larger.
ruslik
@Sheldon: wrong again :) on most machines the default size of `int` is the same as the size of pointer, so you need a special machine for that kind of overflow, with huge address space and small ints.
ruslik
@ruslik: no, you were referring to Java code, in which the size of `int` will still be 32 bits. Please read the article carefully before making strong remarks about it.
Sheldon L. Cooper
+24  A: 

Your last approach seems promising. You can improve on that by manually considering the lowest bits of a and b:

unsigned int average = (a / 2) + (b / 2) + (a & b & 1);

This gives the correct results in case both a and b are odd.

sellibitze
s/agerage/average/g
ArunSaha
Awesome, this is exactly the kind of consideration I was looking for.
Tim
Speaking of software patents it appears that patent application: 20090249356 is trying to patent what is well known folklore in the computer industry. CAS-less single producer single consumer circular queues have been known for almost 30 years. (I wrote my first one in the early 80's)I wrote to complain but they said it was too late. I think the patent office should be inundated with "technical hate emails" on this one.
nbourbaki
folone
+2  A: 

What you have is fine, with the minor detail that it will claim that the average of 3 and 3 is 2. I'm guessing that you don't want that; fortunately, there's an easy fix:

unsigned int average = a/2 + b/2 + (a & b & 1);

This just bumps the average back up in the case that both divisions were truncated.

Stephen Canon
+11  A: 

Your method is not correct if both numbers are odd eg 5 and 7, average is 6 but your method #3 returns 5.

Try this:

average = (a>>1) + (b>>1) + (a & b & 1)

with math operators only:

average = a/2 + b/2 + (a%2) * (b%2)
iniju
Stephen Canon
Fixed it, thanks :)
iniju
+1 for shift instead of division.
alxx
@alxx: any reasonable compiler will optimize division by two into a shift anyway.
Stephen Canon
Upvoted for awesomeness!
Tim
A: 

Not sure if the is better, but division by powers of 2 can be done with a >> k, so

unsigned int average = ( a >> 1 ) + ( b >> 1 );

Marcus P S
Oh, right, and include the last bit as suggested ... as iniju said
Marcus P S
+1  A: 

If the code is for an embedded micro, and if speed is critical, assembly language may be helpful. On many microcontrollers, the result of the add would naturally go into the carry flag, and instructions exist to shift it back into a register. On an ARM, the average operation (source and dest. in registers) could be done in two instructions; any C-language equivalent would likely yield at least 5, and probably a fair bit more than that.

Incidentally, on machines with shorter word sizes, the differences can be even more substantial. On an 8-bit PIC-18 series, averaging two 32-bit numbers would take twelve instructions. Doing the shifts, add, and correction, would take 5 instructions for each shift, eight for the add, and eight for the correction, so 26 (not quite a 2.5x difference, but probably more significant in absolute terms).

supercat
+6  A: 

If you don't mind a little x86 inline assembly, you can do it in three instructions:

unsigned average(unsigned x, unsigned y)
{
    asm("mov 8(%ebp), %eax");
    asm("add 12(%ebp), %eax");
    asm("rcr %eax");
}
FredOverflow
+1: I have never written inline assembly :(, can you please comment and explain the three lines, specially how the values of `x` and `y` are picked up.
ArunSaha
I'd also love to know how this works
Tim
At the start of the inline assembly, there are four 4-byte values on the stack, starting at EBP: EBP+0 (the previous EBP, before the function call), EBP+4 (the previous instruction counter EIP), EBP+8 (x), and EBP+12 (y). The function is expected to place its result in EAX, so the assembly starts by moving x there. It then adds y, and an overflow from this operation will set the carry bit (lack of an overflow will clear the bit). RCR is a rotate-right-with-carry, which rotates EAX one bit to the right (dividing it by two) and shifts the carry bit into the most-sigificant bit of EAX.
Josh Townzen
Reference: http://www.cse.nd.edu/~dthain/courses/cse40243/fall2008/ia32-intro.html (under "Defining Functions"). Also, the calling convention used is `cdecl` (the default for C and non-member C++ functions), which you might want to look up if you want more information.
Josh Townzen
The addition leaves carry bit set when overflow occurs (and one one more bit is needed to hold the result). Then you rotate through carry right (making eax and carry flag effectively 33 bit register) which effectively divides by 2. Then you discard carry flag (which now contains original lowest significand bit from eax) and return eax as the result. Brilliant.
Tomek
@Josh @Tomek: There is no such thing as an *overflow* in unsigned arithmetic, it is called a *carry* (hence the name *carry flag*).
FredOverflow