Portable signbit macro/function?

Does anyone have a ***portable*** version of macro signbit (C99) or can we combine versions that work on each compiler/hardware/endian... or an equivalent C++ function, perhaps called is_negative? (This would just test the signbit for the type(s)). There are several places where this would be useful in dealing properly with NaNs and zero and infinity etc. Thanks Paul PS Must be Boost license of course. --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

Paul A Bristow wrote:
Does anyone have a ***portable*** version of macro signbit (C99)
or can we combine versions that work on each compiler/hardware/endian...
or an equivalent C++ function, perhaps called is_negative?
(This would just test the signbit for the type(s)).
There are several places where this would be useful in dealing properly with NaNs and zero and infinity etc.
Thanks
Paul
PS Must be Boost license of course.
How about bool is_negative(float x) { const float one = 1; const float neg_one = -1; const int one_bits = reinterpret_cast<const int&>(one); const int neg_one_bits = reinterpret_cast<const int&>(neg_one); const int sign_mask = one_bits ^ neg_one_bits; return reinterpret_cast<int&>(x) & sign_mask; } --Johan Råde

| -----Original Message----- | From: boost-bounces@lists.boost.org | [mailto:boost-bounces@lists.boost.org] On Behalf Of Johan Råde | Sent: 02 August 2006 16:08 | To: boost@lists.boost.org | Subject: Re: [boost] Portable signbit macro/function? | | Paul A Bristow wrote: | > Does anyone have a ***portable*** version of macro signbit (C99) | > | > or can we combine versions that work on each | compiler/hardware/endian... | > | > or an equivalent C++ function, perhaps called is_negative? | > | > (This would just test the signbit for the type(s)). | > | > There are several places where this would be useful in | dealing properly with | > NaNs and zero and infinity etc. | > | > Thanks | > | > Paul | > | > PS Must be Boost license of course. | | How about | | bool is_negative(float x) | { | const float one = 1; | const float neg_one = -1; | const int one_bits = reinterpret_cast<const int&>(one); | const int neg_one_bits = reinterpret_cast<const | int&>(neg_one); | const int sign_mask = one_bits ^ neg_one_bits; | | return reinterpret_cast<int&>(x) & sign_mask; | } Sneaky - and looks plausible - but may be quicker to use a macro, if available, as I suspect it is for many platforms. Need to check it works for negative zero and NaNs too - I may get a moment to check this out, for my sins - since I raised the issue ;-) Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

Paul A Bristow wrote:
Does anyone have a ***portable*** version of macro signbit (C99)
or can we combine versions that work on each compiler/hardware/endian...
or an equivalent C++ function, perhaps called is_negative?
(This would just test the signbit for the type(s)).
There are several places where this would be useful in dealing properly with NaNs and zero and infinity etc.
Or how about this template<class T> bool is_negative(const T& x) { return x < T(0); } template<> bool is_negative(const float& x) { const float one = 1; const float neg_one = -1; const int one_bits = reinterpret_cast<const int&>(one); const int neg_one_bits = reinterpret_cast<const int&>(neg_one); const int sign_mask = one_bits ^ neg_one_bits; return reinterpret_cast<const int&>(x) & sign_mask; } template<> bool is_negative(const double& x) { return is_negative(static_cast<float>(x)); } template<> bool is_negative(const long double& x) { return is_negative(static_cast<float>(x)); } --Johan Råde

Gennaro Prota wrote:
On Wed, 02 Aug 2006 17:24:14 +0200, Johan Råde <rade@maths.lth.se> wrote:
Or how about this
Or how about learning what is portable, what does C99 say and what (else) does TR1 (and perhaps IEEE 754) say?
-- [ Gennaro Prota, C++ developer for hire ]
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
The code I suggested is intended to be used when there is no C99 or complete TR1 implementation available. The code should work on any IEEE 754 compliant processor, but stands a good chance of working on processors that are not. --Johan

On Wed, 02 Aug 2006 18:02:34 +0200, Johan Råde <rade@maths.lth.se> wrote:
The code I suggested is intended to be used when there is no C99 or complete TR1 implementation available.
Regardless of whether it is correct or not, I apologize for my tone. I mistook you for the guy who attacked me about the lexical_cast change, hence my reaction, sorry. -- [ Gennaro Prota, C++ developer for hire ]

Gennaro Prota wrote:
On Wed, 02 Aug 2006 18:02:34 +0200, Johan Råde <rade@maths.lth.se> wrote:
The code I suggested is intended to be used when there is no C99 or complete TR1 implementation available.
Regardless of whether it is correct or not, I apologize for my tone. I mistook you for the guy who attacked me about the lexical_cast change, hence my reaction, sorry.
Gennaro, You are right, I really should learn more about C99 and TR1. John Maddock and Paul Bristow have educated me a somewhat. On the other hand, I really have other things to do with my time than to research what the C99 standard has to say about negative zeroes. --Johan

Paul A Bristow wrote:
Does anyone have a ***portable*** version of macro signbit (C99)
Not quite, but I recently added a rather brain dead version to the Math-Toolkit code, just a: template <class T> int signbit(const T& v) { return v < 0 ? 1 : 0; } To be honest that's not very useful :-( What I do find more useful is: template <class T> int sign(const T& v) { return v < 0 ? -1 : 1; } which is invaluable when you need to compare the sign of two (or more) numbers but are concerned about numeric overflow/underflow in the usual a*b < 0 trick. John.

John Maddock wrote: -Toolkit code, just a:
template <class T> int signbit(const T& v) { return v < 0 ? 1 : 0; }
To be honest that's not very useful :-(
What I do find more useful is:
template <class T> int sign(const T& v) { return v < 0 ? -1 : 1; }
which is invaluable when you need to compare the sign of two (or more) numbers but are concerned about numeric overflow/underflow in the usual a*b < 0 trick.
Will not handle negative zero and negative NaN. Will probably handle negative infinity. --Johan

On Wed, 2 Aug 2006 16:53:12 +0100, "John Maddock" <john@johnmaddock.co.uk> wrote:
Paul A Bristow wrote:
Does anyone have a ***portable*** version of macro signbit (C99)
Not quite, but I recently added a rather brain dead version to the Math-Toolkit code, just a:
template <class T> int signbit(const T& v) { return v < 0 ? 1 : 0; }
To be honest that's not very useful :-(
What I do find more useful is:
template <class T> int sign(const T& v) { return v < 0 ? -1 : 1; }
which is invaluable when you need to compare the sign of two (or more) numbers but are concerned about numeric overflow/underflow in the usual a*b < 0 trick.
TR1 asks for a non-macro version: template<class T> bool signbit(T x); Like its C99 counterpart (which formally yields an int, but the behavior is bool-like: nonzero iff the argument value has negative sign) it shall work for any floating point value, including, if supported, signed zeros, infinities and NaNs. I didn't try but I guess the only slightly tricky point to watch for is signaling NaNs. Note that all this is quite different, in scope and semantics, from your sign facility, which BTW could perhaps be "extended" to: template <class T> int sign(const T& v) { return v < 0 ? -1 : (v > 0 ? 1 : 0) } -- [ Gennaro Prota, C++ developer for hire ]

Gennaro Prota wrote:
TR1 asks for a non-macro version:
template<class T> bool signbit(T x);
Like its C99 counterpart (which formally yields an int, but the behavior is bool-like: nonzero iff the argument value has negative sign) it shall work for any floating point value, including, if supported, signed zeros, infinities and NaNs. I didn't try but I guess the only slightly tricky point to watch for is signaling NaNs.
And the endianness of the float's if you're doing bit testing. But of course if there is a macro already it's easy enough to convert it into a function.
Note that all this is quite different, in scope and semantics, from your sign facility, which BTW could perhaps be "extended" to:
template <class T> int sign(const T& v) { return v < 0 ? -1 : (v > 0 ? 1 : 0) }
I know, but I'm not sure about it :-) Or at least it complicates the uses I had for it, maybe I'll demote it to a detail... John.

John Maddock wrote:
And the endianness of the float's if you're doing bit testing. But of course if there is a macro already it's easy enough to convert it into a function.
The following function does bit testing, without advance knowledge of which bit is the sign bit. The function figures out on its own, at compile time, which bit is the sign bit. Hence you do not have to worry about IEEE 754 and endianness. The only thing that is required is that sizeof(float) == sizeof(int). bool signbit(float x) { const float one = 1; const float neg_one = -1; const int sign_mask = reinterpret_cast<const int&>(one) ^ reinterpret_cast<const int&>(neg_one); return reinterpret_cast<int&>(x) & sign_mask; } I have tested it on VC++ 7.1 with signed zeroes, signed infinities and signed NaNs, and plain signed numbers. --Johan Råde

Le jeudi 03 août 2006 à 13:37 +0200, Johan Råde a écrit :
The following function does bit testing, without advance knowledge of which bit is the sign bit. The function figures out on its own, at compile time, which bit is the sign bit. Hence you do not have to worry about IEEE 754 and endianness.
Actually, you do have to worry about IEEE-754. Without it, you have no guarantee that the number representation has the correct properties. For example, with floating-point numbers that rely on a two-complement representation of the mantissa, you will get that +7 is negative (since at least one of its bits will be covered by sign_mask).
The only thing that is required is that sizeof(float) == sizeof(int).
bool signbit(float x) { const float one = 1; const float neg_one = -1; const int sign_mask = reinterpret_cast<const int&>(one) ^ reinterpret_cast<const int&>(neg_one); return reinterpret_cast<int&>(x) & sign_mask; }
You are invoking undefined behavior here: you are accessing a float through a reference to an int. As a consequence, GCC produces code that accesses uninitialized memory (since one and neg_one are optimized away) and returns random values. You have to use char* pointers and memcopies so that there is no aliasing issue. Best regards, Guillaume

Guillaume Melquiond wrote:
bool signbit(float x) { const float one = 1; const float neg_one = -1; const int sign_mask = reinterpret_cast<const int&>(one) ^ reinterpret_cast<const int&>(neg_one); return reinterpret_cast<int&>(x) & sign_mask; }
You are invoking undefined behavior here: you are accessing a float through a reference to an int. As a consequence, GCC produces code that accesses uninitialized memory (since one and neg_one are optimized away) and returns random values. You have to use char* pointers and memcopies so that there is no aliasing issue.
Best regards,
Guillaume
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Thanks for the information. All I need to do is to get my hands on the bit patterns of 1.0f, -1.0f and x. Do I really have to copy the memory to do that? That seems like overkill. Can't I just form pointers to the float constants, cast the pointers to int pointers, and dereference them? Something like
bool signbit(float x) { const float one = 1; const float neg_one = -1; const int sign_mask = *static_cast<const int*>(&one) ^ *static_cast<const int*>(&neg_one); return *static_cast<int*>(&x) & sign_mask; }
--Johan

Le jeudi 03 août 2006 à 17:49 +0200, Johan Råde a écrit :
All I need to do is to get my hands on the bit patterns of 1.0f, -1.0f and x. Do I really have to copy the memory to do that? That seems like overkill.
You don't have to copy the memory, you can simply read the bit patterns char by char, if you prefer. Any other kind of access will produce wrong assembly code on compilers with "strong" optimizers.
Can't I just form pointers to the float constants, cast the pointers to int pointers, and dereference them?
No you can't. See paragraph 3.10/15 of the C++ standard. Best regards, Guillaume

On Thu, 3 Aug 2006 11:35:19 +0100, "John Maddock" <john@johnmaddock.co.uk> wrote:
And the endianness of the float's if you're doing bit testing.
Which shouldn't be a problem for you ;-) Or just position/bitmask of the sign bit.
But of course if there is a macro already it's easy enough to convert it into a function.
Yep. Even if no macro exists you can perhaps hide the machine-dependent stuff in one single "low-level primitive" function upon which you can build all (or much of) the rest. I'm thinking to something like // your namespace... enum type { finite, zero, nan, inf }; template <typename T> type get_type(T t); Also you could probably automatically generate much of the system dependent stuff, for the most common systems. For instance: // widely (not completely) portable void calculate() { typedef float T; // for instance typedef unsigned char byte; T plus_1 ( 1 ); T minus_1( -1 ); const volatile byte * b_plus = reinterpret_cast<const volatile byte *>(&plus_1); const volatile byte * b_minus = reinterpret_cast<const volatile byte *>(&minus_1); int i = 0; int bit = 0; bool found = false; for (i = 0; i < sizeof( T ); ++i ) { for (bit = 0; bit < CHAR_BIT; ++bit) { const int mask = 1 << bit; if( (b_plus[i] & mask) != (b_minus[i] & mask) ) { assert(found == false); found = true; } } } assert(found == true); std::cout << "sign bit at byte " << (i-1) <<", bit " << (bit-1) << '\n' ; } Ok, that will never compile but should give the idea. If any of the asserts triggers just pretend nothing happened, calmly shut the computer down and do not walk... run! -- [ Gennaro Prota, C++ developer for hire ]

On Thu, 03 Aug 2006 16:59:25 +0200, Gennaro Prota <gennaro_prota@yahoo.com> wrote:
For instance:
// widely (not completely) portable void calculate() [...]
Sorry I was in a hurry when I wrote that. This is better: void calculate() // very meaningful name that I // preferred to keep { typedef float T; typedef unsigned char byte; T plus_1( 1 ); T minus_1( -1 ); const volatile byte * b_plus = reinterpret_cast<const volatile byte *>(&plus_1); const volatile byte * b_minus = reinterpret_cast<const volatile byte *>(&minus_1); bool found = false; int pos = 0; byte mask = 0; for (int i = 0; i < sizeof( T ); ++i ) { if ( b_plus[i] != b_minus[i] ) { assert( !found ); mask = b_plus[i] ^ b_minus[i]; pos = i; found = true; assert( (mask & (mask-1)) == 0 ); } } assert(found == true); std::cout << "sign bit at byte " << pos <<", mask is " << static_cast<unsigned>( mask ) << '\n' ; } -- [ Gennaro Prota, C++ developer for hire ]
participants (5)
-
Gennaro Prota
-
Guillaume Melquiond
-
Johan Råde
-
John Maddock
-
Paul A Bristow