C# 将尾数和指数转换为双精度

C# 将尾数和指数转换为双精度,c#,floating-point,double,exponent,mantissa,C#,Floating Point,Double,Exponent,Mantissa,在一个非常高性能的应用程序中,我们发现CPU计算长算术的速度明显快于双倍运算。然而,在我们的系统中,确定我们永远不需要超过9位小数的精度。因此,我们使用longs来表示所有9点精度的浮点算法 但是,在系统的某些部分,由于可读性,使用双工更方便。因此,我们必须将假定小数点后9位的长值转换为双精度 我们发现简单地取长并除以10等于9的幂,或者乘以1除以10等于9的幂,在双精度中给出了不精确的表示 为了解决这个问题,我们使用Math.Round(value,9)给出精确的值 然而,Math.Round

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

但是,在系统的某些部分,由于可读性,使用双工更方便。因此,我们必须将假定小数点后9位的长值转换为双精度

我们发现简单地取长并除以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);

}
[测试]
公共不安全的void ChangeBitsInDouble()
{
原始变量=1.0D;
长比特;
双*dptr=&原件;
//位=*(长*)dptr;
位=位转换器。双字节到64位(原始);
var负值=(位<0);
var指数=(int)((位>>52)和0x7ffL);
变量尾数=位&0xFFFFFFFFFFFL;
如果(指数=0)
{
指数++;
}
其他的
{
尾数=尾数|(1L>=1;
指数++;
}
Console.WriteLine(“尾数”+尾数+”,指数“+指数);
}

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

正如您已经意识到的,根据另一个答案,通过浮点二进制而不是浮点十进制加倍工作,因此初始方法不起作用

也不清楚它是否可以使用一个经过刻意简化的公式,因为不清楚你需要的最大范围是什么,所以舍入成为不可避免的

快速但准确地进行转换的问题已经得到了很好的研究,并且经常得到CPU指令的支持。您唯一能够击败内置转换的机会是:

  • 你在数学上取得了突破,值得写一些严肃的论文
  • 您排除了在您自己的示例中不会出现的足够多的情况,这些情况虽然内置的功能更好,但您的示例通常是针对您自己的使用进行优化的
  • 除非您使用的值的范围非常有限,否则在双精度IEEE 754和长整数之间进行转换时出现短路的可能性会越来越小

    如果您必须覆盖IEEE 754覆盖的大多数情况,或者甚至相当大的比例,那么您最终会使事情变得更慢

    我建议您要么继续使用现有的工具,移动那些尽管不方便,但仍然可以使用long的
    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
    慢很多(正是因为它实现了一种与你在开始问题中类似的方法,但具有不同的指数和更大的尾数)。但是,如果您只需要一种方便的方法来获取用于显示或呈现为字符串的值,那么手动将转换为
    十进制
    比手动将转换为
    双精度
    更具优势,因此它可能值得一看。您确定您拥有的值可以在
    双精度中准确表示吗e
    ?也许这会有帮助,我不想为了帮助你而阅读所有内容:Thansk我们刚刚意识到上面的代码不完整。并且了解到以位表示的双指数使用的是二进制指数而不是十进制指数。因此,你的答案很有意义。这将使长的表示无法读取t将极大地提高性能,转换回双精度将很快。因此,这是一个很好的折衷。谢谢!这似乎是合乎逻辑的,但在测试中,除以转换回双精度的因子后,结果是不精确的:var convert=1ah,,更多的测试…2^30将长时间转换回双精度到长时间生产确切的long值。但是,转换为long back to double的double与原来的double不匹配。您还有什么建议可以解决这个问题吗?在使用double的代码中,性能仍然非常重要。只是它们是用户插件,用户希望使用浮点数。使用decimal当然是非常重要的对用户来说很实用,但对他们所做的任何事情的性能都令人失望。因为我们所有的输入都来自双精度(从字符串或双精度记录为二进制),所以我想提取尾数…用它做数学运算…然后简单地替换输出的尾数。这将有望避免四舍五入和精度不足。