问题
I was curious about the implementation and representation of NaN
in both IEEE single- and double-precision floating points, and I found this implementation of an "is NaN" function. Namely:
int isnan(double x)
{
int32_t hx,lx;
// Move lower 32 bits of double to lx, higher 32 to hx.
EXTRACT_WORDS(hx,lx,x);
// Remove sign bit, since -NaN and NaN are both NaN.
hx &= 0x7fffffff;
// Equivalent to hx |= (lx != 0).
hx |= (u_int32_t)(lx|(-lx))>>31;
// Difference is negative iff (hx & 0x7ff00000) == 0x7ff00000 and (hx & 0x000fffff) != 0.
hx = 0x7ff00000 - hx;
return (int)((u_int32_t)(hx))>>31;
}
I didn't understand the purpose of (lx|(-lx)) >> 31
, and after failing to reason it out in my head, I tested it on all integers, and found it results in 0 for lx = 0
and 1 otherwise.
The only reasons I could come up with are that perhaps using (lx != 0)
instead was not possible due to some C standard not defining what integer value is assigned to true operations (e.g. not guaranteed to be 1 for true) or that perhaps !=
is slower than the negative-or-and-bit-shift. Otherwise, I'm stumped.
For reference, the code I used to try all integers, in case of errors.
#include <stdio.h>
#include <stdint.h>
int main(void) {
int32_t i = 0;
do {
if (((uint32_t)(i | (-i)) >> 31) == 0)
printf("%d\n", i); // prints only 0
} while (i++ != 0xFFFFFFFF); // overflows to -max_int and then climb to -1
return 0;
}
回答1:
The expression (u_int32_t)(lx|(-lx))>>31
is equivalent to lx==0? 0:1
.
However, with lx==0? 0:1
, you are imposing a branch operation into the object code.
This might yield reduced performance in comparison with a couple of bit-wise operations.
It really depends on the underlying HW architecture as well as the designated compiler at hand.
But it will for sure lead to inconsistent performance, depending on branch-prediction heuristics.
The running time of (u_int32_t)(lx|(-lx))>>31
is guaranteed to be identical on every execution.
来源:https://stackoverflow.com/questions/25905263/purpose-of-bitwise-or-of-an-integer-with-its-negative