问题
Is there an easy way to determine the sign of a floating point number?
I experimented and came up with this:
#include <iostream>
int main(int argc, char** argv)
{
union
{
float f;
char c[4];
};
f = -0.0f;
std::cout << (c[3] & 0x10000000) << "\n";
std::cin.ignore();
std::cin.get();
return 0;
}
where (c[3] & 0x10000000) gives a value > 0 for a negative number but I think this requires me to make the assumptions that:
- The machine's bytes are 8 bits big
- a float point number is 4 bytes big?
- the machine's most significant bit is the left-most bit (endianness?)
Please correct me if any of those assumptions are wrong or if I have missed any.
回答1:
Assuming it's a valid floating point number (and not, for example, NaN):
float f;
bool is_negative = f < 0;
It is left as an exercise to the reader to figure out how to test whether a floating point number is positive.
回答2:
Try
float s = copysign(1, f);
from <math.h>
Another helpful thing may be #including <ieee754.h>
, if it's available on your system/compiler.
回答3:
Use signbit() from math.h.
回答4:
1) sizeof(int) has nothing to do with it.
2) assuming CHAR_BIT == 8, yes.
3) we need MSB for that, but endianness affects only byte order, not bit order, so the bit we need to check is c[0]&0x80
for big endianness, or c[3]&0x80
for little, so it would be better to declare union with an uint32_t
and checking with 0x80000000.
This trick have sense only for non-special memory operands. Doing it to a float
value that is in XMM or x87 register will be slower than direct approach. Also, it doesn't treat the special values like NaN or INF.
回答5:
google the floating point format for your system. Many use IEEE 754 and there is specific sign bit in the data to examine. 1 is negative 0 is positive. Other formats have something similar, and as easy to examine.
Note trying to get the compiler to exactly give you the number you want with a hard coded assignment like f = -0.0F; may not work. has nothing to do with the floating point format but has to do with the parser and the C/C++ library used by the compiler. Generating a minus zero may or may not be that trivial in general.
回答6:
I've got this from http://www.cs.uaf.edu/2008/fall/cs441/lecture/10_07_float.html try this:
/* IEEE floating-point number's bits: sign exponent mantissa */
struct float_bits {
unsigned int fraction:23; /**< Value is binary 1.fraction ("mantissa") */
unsigned int exp:8; /**< Value is 2^(exp-127) */
unsigned int sign:1; /**< 0 for positive, 1 for negative */
};
/* A union is a struct where all the fields *overlap* each other */
union float_dissector {
float f;
struct float_bits b;
};
int main() {
union float_dissector s;
s.f = 16;
printf("float %f sign %u exp %d fraction %u",s.f, s.b.sign,((int)s.b.exp - 127),s.b.fraction);
return 0;
}
回答7:
Coming to this late, but I thought of another approach.
If you know your system uses IEEE754 floating-point format, but not how big the floating-point types are relative to the integer types, you could do something like this:
bool isFloatIEEE754Negative(float f)
{
float d = f;
if (sizeof(float)==sizeof(unsigned short int)) {
return (*(unsigned short int *)(&d) >> (sizeof(unsigned short int)*CHAR_BIT - 1) == 1);
}
else if (sizeof(float)==sizeof(unsigned int)) {
return (*(unsigned int *)(&d) >> (sizeof(unsigned int)*CHAR_BIT - 1) == 1);
}
else if (sizeof(float)==sizeof(unsigned long)) {
return (*(unsigned long *)(&d) >> (sizeof(unsigned long)*CHAR_BIT - 1) == 1);
}
else if (sizeof(float)==sizeof(unsigned char)) {
return (*(unsigned char *)(&d) >> (sizeof(unsigned char)*CHAR_BIT - 1) == 1);
}
else if (sizeof(float)==sizeof(unsigned long long)) {
return (*(unsigned long long *)(&d) >> (sizeof(unsigned long long)*CHAR_BIT - 1) == 1);
}
return false; // Should never get here if you've covered all the potential types!
}
Essentially, you treat the bytes in your float as an unsigned integer type, then right-shift all but one of the bits (the sign bit) out of existence. '>>' works regardless of endianness so this bypasses that issue.
If it's possible to determine pre-execution which unsigned integer type is the same length as the floating point type, you could abbreviate this:
#define FLOAT_EQUIV_AS_UINT unsigned int // or whatever it is
bool isFloatIEEE754Negative(float f)
{
float d = f;
return (*(FLOAT_EQUIV_AS_UINT *)(&d) >> (sizeof(FLOAT_EQUIV_AS_UINT)*CHAR_BIT - 1) == 1);
}
This worked on my test systems; anyone see any caveats or overlooked 'gotchas'?
回答8:
Why not if (f < 0.0)
?
来源:https://stackoverflow.com/questions/4235235/sign-of-a-floating-point-number