已签名的浮点变量转换为无符号DWORD

时间:2012-11-18 01:48:58

标签: c++ winapi

我正在尝试将带符号的浮点变量转换为DWORD ... DWORD将被另一个程序使用,因此DWORD变量类型很重要......

首先......签名的DWORD可以解释为无符号的DWORD ..?

也...我如何将签名的浮点数转换为DWORD(签名)..?

1 个答案:

答案 0 :(得分:6)

  

我正在尝试将带符号的浮点变量转换为DWORD ... DWORD将被另一个程序使用,因此DWORD变量类型很重要......

     

首先......签名的DWORD可以解释为无符号的DWORD ..?

     

还...我怎么能将签名的浮点数转换为DWORD(签名)..?

DWORD是由Windows API定义的32位无符号整数类型。没有“签名的DWORD”这样的东西。可能你的意思是对应的签名类型。

在C ++中,所有浮点类型(floatdoublelong 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,应该始终使用恕我直言)