在一个非常高性能的应用程序中,我们发现CPU可以比双精度计算更长的算术运算速度。然而,在我们的系统中,我们确定我们从不需要超过9位小数的精度。所以我们使用long来进行所有浮点运算,理解了9点精度。
然而,在系统的某些部分,由于可读性与双打一起工作,因此更方便。因此,我们必须将假设9位小数的长值转换为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);
}
答案 0 :(得分:1)
你不应该使用10 ^ 9的比例因子,你应该使用2 ^ 30。
答案 1 :(得分:0)
正如你已经意识到的那样,根据另一个答案,双精度工作的是浮点二进制而不是浮点十进制,因此初始方法不起作用。
还不清楚它是否可以使用故意简化的公式,因为不清楚你需要的最大范围是什么,因此舍入变得不可避免。
快速但精确地执行此操作的问题已得到充分研究,并且通常由CPU指令支持。您击败内置转换的唯一机会是:
除非您使用的值范围非常有限,否则双精度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
转换为手工操作,因此可能值得一看。