双精度到十进制,15位后没有舍入

Ste*_*uhr 7 c# precision double decimal

将"高"精度Double转换为Decimal时,由于Rounding,我将使用Convert.ToDecimal或转换为(Decimal)而失去精度.

示例:

double d = -0.99999999999999956d;
decimal result = Convert.ToDecimal(d); // Result = -1
decimal result = (Decimal)(d); // Result = -1
Run Code Online (Sandbox Code Playgroud)

Convert.ToDecimal(double)返回的Decimal值最多包含15位有效数字.如果value参数包含超过15个有效数字,则使用舍入舍入为最接近的数字.

所以为了保持我的精度,我必须将我的double转换为String然后调用Convert.ToDecimal(String):

decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d
Run Code Online (Sandbox Code Playgroud)

这个方法是有效的,但是我想避免使用String变量来将Double转换为Decimal而不用15位后的舍入?

Pas*_*uoq 4

一种可能的解决方案是分解d为 n 个双精度数的精确总和,其中最后一个很小,包含转换为十进制时所需的所有尾随有效数字,而第一个 (n-1) 则精确转换为十进制。

d对于-1.0 和 1.0 之间的源双精度数:

    decimal t = 0M;
    bool b = d < 0;
    if (b) d = -d;
    if (d >= 0.5) { d -= 0.5; t = 0.5M; }
    if (d >= 0.25) { d -= 0.25; t += 0.25M; }
    if (d >= 0.125) { d -= 0.125; t += 0.125M; }
    if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; }
    t += Convert.ToDecimal(d);
    if (b) t = -t;
Run Code Online (Sandbox Code Playgroud)

在 ideone.com 上测试一下。

请注意,即使 C# 以比(它允许自己执行的)d -=更高的精度计算二进制浮点运算,这些运算也是精确的。double

这比从字符串到字符串的转换更便宜double,并且在结果中提供了一些额外的精度位数(上述四个 if-then-else 的精度为四位)。

备注:如果 C# 不允许自己以更高的精度进行浮点计算,一个好技巧是使用 Dekker 拆分来拆分d为两个值d1d2并将每个值精确地转换为十进制。唉,Dekker 分裂仅适用于 IEEE 754 乘法和加法的严格解释。


另一个想法是使用 C# 版本的frexp来获取 的有效数s和指数ed并计算(Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>