我正在尝试将带符号的浮点变量转换为DWORD ... DWORD将被另一个程序使用,因此DWORD变量类型很重要......
首先......签名的DWORD可以解释为无符号的DWORD ..?
也...我如何将签名的浮点数转换为DWORD(签名)..?
答案 0 :(得分:6)
我正在尝试将带符号的浮点变量转换为DWORD ... DWORD将被另一个程序使用,因此DWORD变量类型很重要......
首先......签名的DWORD可以解释为无符号的DWORD ..?
还...我怎么能将签名的浮点数转换为DWORD(签名)..?
DWORD
是由Windows API定义的32位无符号整数类型。没有“签名的DWORD”这样的东西。可能你的意思是对应的签名类型。
在C ++中,所有浮点类型(float
,double
和long double
)都是有符号类型,所以谈论“签名浮点数”是不寻常的”。大概你的意思是它可以是负值。将负浮点值转换为无符号整数有两种主要的可能性:
好像将浮点值(比如-1.23
)转换为有符号整数,然后通过2 n <的适当倍数进行调整/ i> 将其带入无符号整数范围,或
好像浮点值再次说-1.23
,通过2 n 的适当倍数调整,使其进入无符号整数范围,然后转换为无符号整数类型。
这些过程通常会产生不同的结果,差异为1.但是,测试它,它是Visual C ++ 11.0和MinGW g ++ 4.7.1使用的第一个过程。而且我怀疑这是标准强制要求的(例如,C ++ 11最终得到了关于负数整数除法结果的明确规则):
#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;
}
输出,使用Visual C ++ 11.0和MinGW g ++ 4.7.1:
4294967295 4294967295 1 4294967294 2
通常,转换(通过简单分配或初始化执行)会丢失数据。
编译器可能会对此发出警告。然后,使其闭合的一种可能方式(可能或可能不起作用)是使用static_cast
来明确转换。无论如何,请注意,转换必须在一般情况下丢失数据,因为通常浮点值的位数多于DWORD
。
然而,有一种情况,DWORD
有足够的位,而且类型为float
的值,在Windows中是32位IEEE。
因此,您可以使用相同的位将任何float
值表示为DWORD
值,但数值之间的连接看起来非常随意。它是否实际有用取决于您的其他应用。它有什么期望,或它能处理什么?
#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;
}
输出,使用Visual C ++ 11和g ++ 4.7.1(实际上任何实用的Windows C ++编译器):
-1.23 3214766244 -1.23
但请注意,虽然后一种转换在Windows 中定义,但C ++标准不支持Windows规则。从严格的形式来看,不恰当地将上述代码视为与平台无关,上面打破了C ++的严格别名规则。哪位g ++非常乐意通知您:
[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] > _
为了让像g ++这样的愚蠢的编译器感到高兴,你必须通过字节缓冲区进行转换。这非常令人讨厌,因为警告是关于编译器检测的所有内容,表示您所希望的内容,与它可能适用的一些非常不受欢迎的边际优化不兼容。当它能够检测到它不应该时,为什么它不能不做那些不受欢迎的事情,而不是警告它无法做到这一点?或者,为什么它根本不能不做呢?因为没有人想要它。
但无论如何,这里是让g ++闭嘴的代码:
#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;
}
当然,由于代码更冗长,效率更低,人们可能会认为关闭一个愚蠢的编译器(即g ++)是不值得的。就个人而言,我认为不值得。相反,只需说出-fno-strict-aliasing
到g ++,然后使用reinterpret_cast
,因为这就是
总结一下,如果你的其他程序期望数值,如果可能的话接近原始浮点值,那么只需通过分配或初始化转换为DWORD
,可能使用static_cast
来禁止编译器警告。
如果您的其他程序需要float
值的位,那么只需使用reinterpret_cast
转换。上面的代码显示了如何使用效率更低,更详细和吸引bug的memcpy
来代替g ++编译器。但我的建议是,如果这是相关的,那么只需通过-fno-strict-aliasing
来抑制g ++警告(另一个有用的选项,使g ++编译器符合实用性,是-fwrapv
,应该始终使用恕我直言)