8

在一个非常高性能的应用程序中,我们发现 CPU 计算长算法的速度明显快于双精度数。但是,在我们的系统中,我们确定我们永远不需要超过 9 位小数的精度。所以我们使用 longs 来理解所有精度为 9 点的浮点运算。

然而,在系统的某些部分,由于使用双打的可读性,它更方便。因此,我们必须将假定小数点后 9 位的 long 值转换为 double。

我们发现简单地取长并除以 10 的 9 次方或乘以 1 除以 10 的 9 次方给出了双精度的不精确表示。

为了解决这个问题,我们使用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);

}
4

2 回答 2

1

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

于 2012-01-19T13:43:39.743 回答
0

正如您根据其他答案已经意识到的那样,双打通过浮点二进制而不是浮点十进制工作,因此初始方法不起作用。

也不清楚它是否可以使用故意简化的公式,因为不清楚你需要的最大范围是多少,所以舍入变得不可避免。

快速但精确地这样做的问题得到了充分研究,并且经常得到 CPU 指令的支持。您击败内置转换的唯一机会是:

  1. 你在数学上取得了突破,值得写一些关于它的严肃论文。
  2. 您排除了在您自己的示例中不会出现的足够多的情况,虽然内置插件更好,但通常您的插件针对您自己的使用进行了优化。

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

如果您必须涵盖 IEEE 754 涵盖的大多数情况,甚至其中相当大的一部分,那么您最终会让事情变得更慢。

我建议要么保留你所拥有的,double尽管有不便,但移动更方便长时间坚持的案例,或者在必要时使用decimal. 您可以使用以下方法轻松创建decimal一个long

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,因此值得一看。

于 2012-01-19T15:27:49.983 回答