我正在尝试将带符号的浮点变量转换为 DWORD ... DWORD 将由另一个程序使用,因此 DWORD 变量类型很重要...
首先...可以将带符号的 DWORD 解释为无符号的 DWORD ..?
还...如何将带符号的浮点数转换为 DWORD(带符号)..?
我正在尝试将带符号的浮点变量转换为 DWORD ... DWORD 将由另一个程序使用,因此 DWORD 变量类型很重要...
首先...可以将带符号的 DWORD 解释为无符号的 DWORD ..?
还...如何将带符号的浮点数转换为 DWORD(带符号)..?
DWORD
是由 Windows API 定义的 32 位无符号整数类型。没有“签名的 DWORD”之类的东西。可能您的意思是相应的签名类型。
在 C++ 中,所有浮点类型(float
和double
)long double
都是有符号类型,因此谈论“有符号浮点”是不寻常的。大概你的意思是它可以是一个负值。将负浮点值转换为无符号整数有两种主要可能性:
就好像浮点值,比如说-1.23
,被转换为有符号整数,然后通过 2 n的适当倍数进行调整,使其进入无符号整数范围,或者
就好像浮点值,再次说,通过 2 n-1.23
的适当倍数调整,使其进入无符号整数范围,然后转换为无符号整数类型。
这些过程通常会产生不同的结果,相差 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
通常,转换(通过简单的赋值或初始化执行)会丢失数据。
编译器可能会对此发出警告。使其关闭的一种可能方法(可能有效也可能无效)是使用 astatic_cast
使转换显式。无论如何,请注意在一般情况下转换必须丢失数据,因为通常浮点值比DWORD
.
然而,在一种情况下, aDWORD
有足够的位,这是一个 type 的值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:\开发\测试] > gnuc --strict-aliasing foo.cpp foo.cpp:在函数“DWORD dwordBitsFrom(float)”中: foo.cpp:7:55: 警告:取消引用类型双关指针将破坏严格别名规则 [-Wstrict-aliasing] foo.cpp:在函数'float floatFromBits(DWORD)'中: foo.cpp:12:53:警告:取消引用类型双关指针将破坏严格别名规则 [-Wstrict-aliasing] [D:\开发\测试] > _
为了让 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
可能使用 astatic_cast
来抑制编译器警告。
如果您的其他程序需要一个值的位float
,那么只需使用reinterpret_cast
转换。上面的代码展示了如何使用更低效、更冗长和更容易吸引错误的代码memcpy
来取悦 g++ 编译器。但我的建议是,如果这是相关的,那么只需通过-fno-strict-aliasing
(另一个使 g++ 编译器符合实用性的有用的此类选项是-fwrapv
,恕我直言,应该始终使用它)来抑制 g++ 警告。