Reputation: 809
I am trying to convert a signed floating point variable to DWORD...the DWORD is to be used by another program so the DWORD variable type is important...
firstly...can a signed DWORD be interpreted as an unsigned DWORD..?
also...how can i convert a signed float to DWORD(signed)..?
Upvotes: 0
Views: 6771
Reputation: 145349
I am trying to convert a signed floating point variable to DWORD...the DWORD is to be used by another program so the DWORD variable type is important...
firstly...can a signed DWORD be interpreted as an unsigned DWORD..?
also...how can i convert a signed float to DWORD(signed)..?
DWORD
is a 32-bit unsigned integer type defined by the Windows API. There is no such thing as a “signed DWORD”. Possibly you mean a corresponding signed type.
In C++ all floating point types (float
, double
and long double
) are signed types, so it’s unusual to talk about a “signed floating point”. Presumably you mean that it can be a negative value. And there are two main possibilities for converting a negative floating point values to unsigned integer:
as if the floating point value, say -1.23
, is converted to signed integer, and then adjusted by a suitable multiple of 2n to bring it into the unsigned integer range, or
as if the floating point value, again say -1.23
, is adjusted by a suitable multiple of 2n to bring it into the unsigned integer range, and then converted to the unsigned integer type.
These procedures will generally yield different results, differing by 1. However, testing it, it's the first procedure that's used by Visual C++ 11.0 and MinGW g++ 4.7.1. And I suspect that somehow this is mandated by the standard (e.g., C++11 finally got clear rules about the result of integer division of negative numbers):
#include <math.h>
#include <windows.h>
using namespace std;
#include <iostream>
DWORD test1()
{
double floatValue = -1.23456;
DWORD const dwValue = floatValue;
cout << dwValue << endl;
return dwValue;
}
DWORD test2()
{
double floatValue = -1.23456;
double reduced = floatValue + (1uLL << 32);
DWORD const dwValue = reduced;
cout << dwValue << endl;
return dwValue;
}
int main()
{
cout << DWORD(-1) << endl;
DWORD const t1 = test1(); cout << 1uLL + DWORD(-1) - t1 << endl;
DWORD const t2 = test2(); cout << 1uLL + DWORD(-1) - t2 << endl;
}
Output, with Visual C++ 11.0 and MinGW g++ 4.7.1:
4294967295 4294967295 1 4294967294 2
Generally, the conversion (performed by simple assignment or initialization) loses data.
The compiler may warn about that. One possible way to make it shut up, which may or may not work, is then to use a static_cast
to make the conversion explicit. Anyway, be aware that the conversion must lose data in the general case, because in general a floating point value has more bits than a DWORD
.
There is one case, however, where a DWORD
has enough bits, and that's for a value of type float
, which in Windows is 32-bit IEEE.
You can therefore represent any float
value as a DWORD
value with the same bits, but the connection between numerical values will then seem pretty arbitrary. And whether it will be practically useful depends on your other application. What does it expect, or what can it handle?
#include <windows.h>
#include <iostream>
using namespace std;
DWORD dwordBitsFrom( float const number )
{
return *reinterpret_cast< DWORD const* >( &number );
}
float floatFromBits( DWORD const bits )
{
return *reinterpret_cast< float const* >( &bits );
}
int main()
{
float const original = -1.23;
DWORD const bits = dwordBitsFrom( original );
float const reconstituted = floatFromBits( bits );
cout << original << endl;
cout << bits << endl;
cout << reconstituted << endl;
}
Output, with Visual C++ 11 and g++ 4.7.1 (and indeed any practically useful Windows C++ compiler):
-1.23 3214766244 -1.23
Note, however, that while this latter conversion is well defined in Windows, the Windows rule is not supported by the C++ standard. From a strictly formal point of view, inappropriately regarding the above code as platform-independent, the above breaks the strict aliasing rules of C++. Which g++ is very happy to inform you about:
[D:\dev\test] > gnuc --strict-aliasing foo.cpp foo.cpp: In function 'DWORD dwordBitsFrom(float)': foo.cpp:7:55: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] foo.cpp: In function 'float floatFromBits(DWORD)': foo.cpp:12:53: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] [D:\dev\test] > _
In order to make silly compilers such as g++ happy, you have to do the conversion via a buffer of bytes. It's pretty annoying, because the warning is all about the compiler detecting that what you have expressed that you want, is not compatible with some very undesirable marginal optimizations that it might apply. And when it can detect that it shouldn't, why can't it just not not do that undesirable thing, instead of warning about its inability to do it? Or, why can't it just not not do it at all? Since nobody wants it.
But anyway, here's code to make g++ shut up:
#include <windows.h>
#include <iostream>
#include <string.h> // memcpy
using namespace std;
static_assert( sizeof( DWORD ) == sizeof( float ), "y DWORD no same size float?" );
int const nBytes = sizeof( DWORD );
DWORD dwordBitsFrom( float const number )
{
char buffer[nBytes];
DWORD result;
memcpy( buffer, &number, nBytes );
memcpy( &result, buffer, nBytes );
return result;
}
float floatFromBits( DWORD const bits )
{
char buffer[nBytes];
float result;
memcpy( buffer, &bits, nBytes );
memcpy( &result, buffer, nBytes );
return result;
}
int main()
{
float const original = -1.23;
DWORD const bits = dwordBitsFrom( original );
float const reconstituted = floatFromBits( bits );
cout << original << endl;
cout << bits << endl;
cout << reconstituted << endl;
}
Of course, since it's much more verbose and less efficient code, one may decide that shutting up a silly compiler (namely g++) is not worth it. Personally, I think it's not worth it. Instead, just say -fno-strict-aliasing
to g++, and use the reinterpret_cast
, because that's what it's for, why we have it in the language.
Summing up, if your other program expects a numerical value that if possible is close to the original floating point value, then just convert to DWORD
by assigning or initializing, possibly using a static_cast
to suppress compiler warnings.
And if your other program expects the bits of a float
value, then just use the reinterpret_cast
conversion. The code above shows how to use the more inefficient, verbose and bug-attracting memcpy
instead just to please the g++ compiler. But my advice is, if that's relevant, then just suppress the g++ warning via -fno-strict-aliasing
(another useful such option to bring the g++ compiler into line with practicality, is -fwrapv
, which IMHO should always be used).
Upvotes: 7