fastest c arithmetic method for ints
I am writing a c program. I have an unsigned integer (16bit) whose value could be anything at any time and I have a signed char (8bit) whose value could be anything at any time, within the obvious limits of the data types. I need to add the signed char to the unsigned int, the result being an unsigned int, and if the value overflows either past 0xFFFF or below 0x00, i need the result to equal the limit (either 0x00 or 0xFFFF). I want to know what would be the fastest approach to doing this? My approach is shown below but it uses a long data type and thus long binary arithmetic so I am guessing that there is a faster way...
long i;
unsigned int result;
i = someUINT + someCHAR;
if(i <= 0)
{
result = 0;
}
else if(i >= 0xFFFF)
{
result = 0xFFFF开发者_如何学C;
}
else
{
result = (unsigned int)i;
}
EDIT: I am using a 16bit MCU (PIC24HJ series) and the Microchip C30 compiler.
Almost certainly, the correct answer is
if(i <= 0)
{
result = 0;
}
else if(i >= 0xFFFF)
{
result = 0xFFFF;
}
else
{
result = (unsigned int)i;
}
Profile the application, and if this turns out to be a bottle-neck (which I highly, highly doubt), then rewrite it.
Modern compilers are very good at writing branchless-conditionals for code like this, so just write it the way that makes the most sense and let the compiler do its job. Don't confuse both the compiler and whatever poor person has to read this code in the future by using some convoluted bit-fiddling hack.
You can avoid the long checking before adding:
if(0xFFFF - someUINT < someCHAR) {
return 0xFFFF;
} else {
return someUINT + someCHAR;
}
Of course, if you REALLY need this to be FAST, turn this into an inline function or macro and go assembly.
This algorithm only works in 2's complement.
When checking a signed addition for overflow, the result must have the same sign as at least one of the operands. This case turns out to be only slightly different; if the result flips the "sign" bit then it's OK if both operands have the same "sign" bit. And of course the computation of the unsigned limits is easier!
uint16_t UIntPlusChar(uint16_t u, char ch)
{
int16_t i = (int16_t)u;
int16_t p = i + ch;
if ((ch ^ i) < 0 && (p ^ i) < 0)
p = i >> 15;
return (uint16_t)p;
}
Wow I love stuff like this. Here is my stab assuming that most of the time it will fall in between the boundaries try this
long i;
i= char + int;
if((i & 0xFFFF) == i){
return (int)i;
}
else if(i < 0)
{
return 0;
}
else
{
return 0xFFFF;
}
The fastest way will almost always be to take advantage of processor-specific features that you cannot describe in portable C code. Write manifestly correct portable code that works, and let the compiler do what it will do. If you have specific benchmark data that shows that this specifically must be faster, implement an additional tuned version that is processor-specific.
Many processors (including, I believe, the PIC24) have "saturating add" instructions that perform exactly this operation. The fastest thing is typically to write assembly that uses that instruction specifically, but there's no reason to do so unless you have evidence that the function needs to be faster.
result = someUINT + someCHAR;
if (someCHAR > 0)
{
if (result < someCHAR)
{
result = 0xFFFF;
}
}
else if (result > someUINT)
{
result = 0;
}
I would guess the fastest would be something like:
UInt16 uival; Int8 sbval; UInt16 result; result = uival + sbval; if (uival & 0x8000) /* Only worry about max-val overflow */ { if (result = 65280) /* Underflow */ result = 0; }
Things are a little simplified because any overflows can only happen into a small portion of the numeric range. If the addend were 16 bits, it would be necessary to test the difference between the original uint16 and the result to see if there was overflow; since the addend is only 8 bits, that's not needed. I've not used the PIC24xx parts, so I don't know if testing for 256 or 65280 is faster than other values, but on 8-bit parts it certainly should be.
精彩评论