一种快速方法将一个double加到一个32

在阅读Lua的源代码时,我注意到Lua使用一个macrodouble加到32位int 。 我提取了macro ,它看起来像这样:

union i_cast {double d; int i[2]};
#define double2int(i, d, t)  
    {volatile union i_cast u; u.d = (d) + 6755399441055744.0; 
    (i) = (t)u.i[ENDIANLOC];}

这里ENDIANLOC被定义为字节序, 0表示小端, 1表示大端。 Lua仔细处理排序。 t代表整数类型,如intunsigned int

我做了一点研究,并且有一个更简单的macro格式,它使用相同的思想:

#define double2int(i, d) 
    {double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}

或者以C ++风格:

inline int double2int(double d)
{
    d += 6755399441055744.0;
    return reinterpret_cast<int&>(d);
}

这个技巧可以在任何使用IEEE 754的机器上工作(这意味着今天几乎所有机器都是这样)。 它适用于正数和负数,四舍五入符合银行家守则。 (这并不令人惊讶,因为它遵循IEEE 754.)

我写了一个小程序来测试它:

int main()
{
    double d = -12345678.9;
    int i;
    double2int(i, d)
    printf("%dn", i);
    return 0;
}

如预期的那样,它输出-12345679。

我想详细讨论这个棘手的macro是如何工作的。 幻数6755399441055744.0实际上是2^51 + 2^52 ,或1.5 * 2^52 ,和1.5二进制可以被表示为1.1 。 当任何32位整数被添加到这个幻数时,好吧,我从这里输了。 这个技巧如何工作?

PS:这是在Lua源代码Llimits.h中。

更新

  • 正如@Mysticial所指出的那样,这种方法不会将自己限制为一个32位的int ,只要数字在2 ^ 52的范围内,它也可以扩展为一个64位的int 。 ( macro需要修改。)
  • 有些材料说这种方法不能用于Direct3D。
  • 当与微软汇编的x86工作,有一个更快macro写在assembly (这也从Lua源中提取):

    #define double2int(i,n)  __asm {__asm fld n   __asm fistp i}
    
  • 单精度数有一个相似的幻数: 1.5 * 2 ^23


  • double表示如下:

    它可以看作是两个32位整数; 现在,所有版本的代码中的int (假设它是一个32位int )就是图中右边的那个,所以你最终做的只是最低的32位尾数。


    现在,以魔术数字; 正如你所说的,6755399441055744是2 ^ 51 + 2 ^ 52; 增加这样一个数字迫使double进入2 ^ 52和2 ^ 53之间的“甜蜜范围”,正如维基百科在这里解释的那样,它有一个有趣的属性:

    在252 = 4,503,599,627,370,496和253 = 9,007,199,254,740,992之间,可表示的数字恰好是整数

    这源自尾数为52位的事实。

    关于添加251 + 252的另一个有趣的事实是,它仅在最高两位中影响尾数 - 无论如何都会被丢弃,因为我们只取最低的32位。


    最后但并非最不重要的:标志。

    IEEE 754浮点使用幅度和符号表示,而“正常”机器上的整数使用2的补码算法; 这是如何处理的?

    我们只讨论了正整数; 现在假设我们正在处理一个32位int表示的范围内的负数,所以小于(绝对值)小于(-2 ^ 31 + 1)。 称之为-a 。 通过添加幻数,这样的数字显然是正数,并且结果值是252 + 251 +( - a)。

    现在,如果我们解释2的补码表示中的尾数,我们会得到什么? 它必须是(252 + 251)和(-a)的2的补码和的结果。 同样,第一项只影响高两位,剩下的0〜50位是(-a)的二进制补码表示(再次,减去高两位)。

    由于将2的补码数量减少到更小的宽度只需要切掉左边的多余位,采用低32位就可以正确地(-a)进行32位二进制补码运算。

    链接地址: http://www.djcxy.com/p/72611.html

    上一篇: A fast method to round a double to a 32

    下一篇: bit signed multiplication with a 64