2012-01-19 50 views
8

在一款非常高性能的应用程序中,我们发现CPU可以计算长计算的速度明显快于双精度。但是,在我们的系统中,确定我们从不需要超过9位小数位的精度。所以我们对所有浮点运算都使用long,并且理解了9点精度。将尾数和指数转换为双精度值

但是,在系统的某些部分,由于可读性与双打一起工作,因此更方便。所以我们必须将假设9位小数的长整数值转换为双整数值。

我们发现简单地取长整数除以10的幂乘以9或乘以1除以10的幂后得到不精确的表示。

为了解决这个问题,我们使用Math.Round(value,9)来给出精确的值。

但是,Math.Round()的表现可怕的很慢。

因此,我们现在的想法是直接将尾数和指数转换为双精度的二进制格式 - 这样就不需要四舍五入。

我们已经在网上了解了如何检查双精度的位来得到尾数和指数,但是弄清楚如何通过使用这些位找出尾数和指数并且编制双精度来反转。

有什么建议吗?

[Test] 
public unsafe void ChangeBitsInDouble() 
{ 
    var original = 1.0D; 
    long bits; 
    double* dptr = &original; 
    //bits = *(long*) dptr; 
    bits = BitConverter.DoubleToInt64Bits(original); 
    var negative = (bits < 0); 
    var exponent = (int) ((bits >> 52) & 0x7ffL); 
    var mantissa = bits & 0xfffffffffffffL; 
    if(exponent == 0) 
    { 
     exponent++; 
    } 
    else 
    { 
     mantissa = mantissa | (1L << 52); 
    } 
    exponent -= 1075; 

    if(mantissa == 0) 
    { 
     return; 
    } 

    while ((mantissa & 1) == 0) 
    { 
     mantissa >>= 1; 
     exponent++; 
    } 

    Console.WriteLine("Mantissa " + mantissa + ", exponent " + exponent); 

} 
+3

您确定您拥有的价值完全可以用double来表示吗? – Justin

+0

也许这将有所帮助,我不想全部阅读它只是为了帮助您:P http://steve.hollasch.net/cgindex/coding/ieeefloat.html – MrFox

回答

1

您不应该使用10^9的比例因子,而应该使用2^30来代替。

+0

Thansk我们只是意识到上面的代码不是'完成。并且学习以位表示的双指数使用二进制指数而不是十进制指数。所以你的回答让世界变得有意义。 – Wayne

+0

这会使长时间表示无法读取,但会大大提高性能,并且转换回双倍会快速减轻。所以这是一个很好的折衷。谢谢! – Wayne

+0

好吧,这似乎是合乎逻辑的,但在测试后,除以因子转换回来双倍它出来不精确:var convert = 1 << 30; 双重价格= 45.454945768D; long result1 =(long)(price * convert); double result2 =((double)result1)/ convert; Assert.AreEqual(result2,price); – Wayne

0

正如你已经意识到,根据其他答案,双工工作的浮点二进制而不是浮点小数,因此初始方法不起作用。

目前还不清楚它是否可以使用故意简化的公式,因为目前尚不清楚您需要的最大范围是什么,所以舍入变得不可避免。

这样做很快,但精确的问题已得到充分研究,并且通常由CPU指令支持。您唯一可能击败内置转换的机会是:

  1. 您碰到一个数学突破,值得一些严肃的论文被写入。
  2. 您排除了不会在您自己的示例中出现的足够情况,而内置插件通常更适合您自己的使用。

除非您使用的值范围非常有限,否则双精度IEEE 754与长整数之间转换的快速切换的可能性会越来越小。

如果您现在需要覆盖IEEE 754涵盖的大部分案例,或者相当大一部分案例,那么您最终会让事情变得更慢。

我建议要么保持与你有什么,移动的情况下,double更便利,尽管不方便仍然坚持,或者如果有必要使用decimal。您可以从long创建decimal轻松地:

private static decimal DivideByBillion (long l) 
{ 
    if(l >= 0) 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, false, 9); 
    l = -l; 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, true, 9); 
} 

现在,decimal是幅度较慢的算术使用比double(恰恰是因为它实现了在开放的问题与你类似的做法,但有不同的指数和更大的尾数)。但是,如果您只需要一种方便的方式来获取显示值或呈现字符串的值,那么手动转换为decimal比手动转换为double具有优势,因此可能是值得关注的。

+0

在使用双打的代码中,性能仍然非常重要。这只是它们是用户插件,用户期望用户使用浮点数。使用小数对于用户来说当然是实用的,但对于他们所做的任何事情的表现令人失望。由于我们所有的输入都来自双打(从字符串或双打录制为二进制),所以我正在考虑提取尾数......用数学做...然后简单地替换输出的尾数。这将有希望避免四舍五入和缺乏精确性。 – Wayne