Re: [boost] Portable signbit macro/function?

----Original Message---- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Guillaume Melquiond Sent: 03 August 2006 17:16 To: boost@lists.boost.org Subject: Re: [boost] Portable signbit macro/function?
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
... and of course this whole masking approach wouldn't work for an architecture where the sign of a floating point number was indicated by setting the sign bit such that the parity of all the bits in the number was odd for positive and even for negative! That is not to say that the bit test via mask is not potentially useful, just that one should be aware of its limitations. -- Martin Bonner Martin.Bonner@Pitechnology.com Pi Technology, Milton Hall, Ely Road, Milton, Cambridge, CB4 6WZ, ENGLAND Tel: +44 (0)1223 203894

Martin Bonner wrote:
That is not to say that the bit test via mask is not potentially useful, just that one should be aware of its limitations.
It could be useful on some platforms that do not have a predefined signbit macro/function. The problem we are running into is: How do you convert a value of type S to a value of type T, so that the bitpattern is preserved? (It is of course assumed that sizeof(S) == sizeof(T).) Maybe this task should be put in a separate template, something like template<class T, class S> T binary_cast(const& S s); Gennaro Prota suggests implementing this using reinterpret_cast<const volatile byte*>. Then the signbit test could be implemented, for some platforms, as follows: const std::bitset<sizeof(float)> signbit_mask = binary_cast<std::bitset<sizeof(float)> >(1.0f) ^ binary_cast<std::bitset<sizeof(float)> >(-1.0f); bool signbit(float x) { return (binary_cast<std::bitset<sizeof(float)> >(x) & signbit_mask) == signbit_mask; } This can be templatized, and will handle such things as 80-bit long double. --Johan Råde

Johan Råde wrote:
This can be templatized, and will handle such things as 80-bit long double.
Maybe, don't forget the padding bytes that are often injected into such types (only an issue if the endianness put's the sign bit at the end of the 10-byte sequence rather than the beginning). John.

John Maddock wrote:
Johan Råde wrote:
This can be templatized, and will handle such things as 80-bit long double.
Maybe, don't forget the padding bytes that are often injected into such types (only an issue if the endianness put's the sign bit at the end of the 10-byte sequence rather than the beginning).
John.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I have realized that there are many problems with the code I just suggested. Ignore that suggestion. I'm working on something else.

John Maddock wrote:
Johan Råde wrote:
This can be templatized, and will handle such things as 80-bit long double.
Maybe, don't forget the padding bytes that are often injected into such types (only an issue if the endianness put's the sign bit at the end of the 10-byte sequence rather than the beginning).
John.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
John, I think the following is a reasonable compromise: const boost::uint32_t signbit_mask = binary_cast<boost::uint32_t>(1.0f) ^ binary_cast<boost::uint32_t>(-1.0f); inline bool signbit_impl(float x) { return binary_cast<boost::uint32_t>(x) & signbit_mask; } inline bool signbit_impl(double x) { return sign_bit(static_cast<float>(x)); } inline bool signbit_impl(long double x) { return sign_bit(static_cast<float>(x)); } Here I assume that 1. IEEE 754 or some similar representation 2. sizeof(float) == 4 3. casts between different numeric types preserve the sign bits, also for non-finite numbers. That should cover a large number of platforms. --Johan Råde

There were a few typos. Here is the correct code. const boost::uint32_t signbit_mask = binary_cast<boost::uint32_t>(1.0f) ^ binary_cast<boost::uint32_t>(-1.0f); inline bool signbit_impl(float x) { return binary_cast<boost::uint32_t>(x) & signbit_mask; } inline bool signbit_impl(double x) { return signbit_impl(static_cast<float>(x)); } inline bool signbit_impl(long double x) { return signbit_impl(static_cast<float>(x)); } --Johan Råde

And here is an implementation of binary_cast, based on Gennaro Prota's code. template<class T, class S> T binary_cast(const S& s) { BOOST_STATIC_ASSERT(sizeof(S) == sizeof(T)); T t; std::copy( reinterpret_cast<const volatile char*>(&s), reinterpret_cast<const volatile char*>(&s) + sizeof(S), reinterpret_cast<volatile char*>(&t) ); return t; } I still find it hard to believe that the volatile are needed ;-) But my experience with different compilers is quite limited. --Johan Råde

On Fri, 4 Aug 2006 09:53:35 +0100, "John Maddock" <john@johnmaddock.co.uk> wrote:
Johan Råde wrote:
This can be templatized, and will handle such things as 80-bit long double.
Maybe, don't forget the padding bytes that are often injected into such types (only an issue if the endianness put's the sign bit at the end of the 10-byte sequence rather than the beginning).
There are also platforms that add parity, so you can get two 1 bits in the object representation. As I said, the code is widely portable (probably on all implementations supported by boost) not fully portable. -- [ Gennaro Prota, C++ developer for hire ]

Gennaro Prota wrote:
On Fri, 4 Aug 2006 09:53:35 +0100, "John Maddock" <john@johnmaddock.co.uk> wrote:
Johan Råde wrote:
This can be templatized, and will handle such things as 80-bit long double. Maybe, don't forget the padding bytes that are often injected into such types (only an issue if the endianness put's the sign bit at the end of the 10-byte sequence rather than the beginning).
There are also platforms that add parity, so you can get two 1 bits in the object representation.
As I said, the code is widely portable (probably on all implementations supported by boost) not fully portable.
-- [ Gennaro Prota, C++ developer for hire ]
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Does IEEE 754 allow a parity bit? --Johan Råde

Johan Råde wrote:
Does IEEE 754 allow a parity bit?
In principle, IEEE 754 specifies value bits, not storage bits. There is no guarantee that storage bits represent value bits, they can (in theory) be encrypted or xor'ed with AA55. In practice, though, the 32 value bits of an IEEE 754 float do correspond to the 32 storage bits of a 32 bit unsigned integer.

Peter Dimov wrote:
Johan Råde wrote:
Does IEEE 754 allow a parity bit?
In principle, IEEE 754 specifies value bits, not storage bits. There is no guarantee that storage bits represent value bits, they can (in theory) be encrypted or xor'ed with AA55. In practice, though, the 32 value bits of an IEEE 754 float do correspond to the 32 storage bits of a 32 bit unsigned integer.
Right now parity bits is a hypothetical probem. Let's fix it if it becomes a real problem.

Gennaro Prota wrote:
On Fri, 4 Aug 2006 09:53:35 +0100, "John Maddock"
There are also platforms that add parity, so you can get two 1 bits in the object representation.
As I said, the code is widely portable (probably on all implementations supported by boost) not fully portable.
Do you think that inline bool signbit(float x) { // check that signbit_mask contains at most one 1 bit BOOST_ASSERT(!(signbit_mask & (signbit_mask - 1))); return binary_cast<boost::uint32_t>(x) & signbit_mask; } is a good idea? It seems a bit wasteful to make this check each time you call the function signbit. --Johan Råde

Johan Råde wrote:
Do you think that
inline bool signbit(float x) { // check that signbit_mask contains at most one 1 bit BOOST_ASSERT(!(signbit_mask & (signbit_mask - 1))); return binary_cast<boost::uint32_t>(x) & signbit_mask; }
is a good idea?
The unit tests of signbit should be enough to determine whether it works on a particular platform.
participants (5)
-
Gennaro Prota
-
Johan Råde
-
John Maddock
-
Martin Bonner
-
Peter Dimov