在C#中将十进制转换为双数可导致差异 [英] Conversion of a decimal to double number in C# results in a difference

查看:116
本文介绍了在C#中将十进制转换为双数可导致差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题的总结:



对于一些十进制值,当我们将类型从十进制转换为双精度时,一小部分被添加到结果中。 >

更糟糕的是,转换后可能会有两个相等的十进制值导致不同的双重值。



代码示例:

  decimal dcm = 8224055000.0000000000m; // dcm = 8224055000 
double dbl = Convert.ToDouble(dcm); // dbl = 8224055000.000001

decimal dcm2 = Convert.ToDecimal(dbl); // dcm2 = 8224055000
double dbl2 = Convert.ToDouble(dcm2); // dbl2 = 8224055000.0

decimal deltaDcm = dcm2 - dcm; // deltaDcm = 0
double deltaDbl = dbl2 - dbl; // deltaDbl = -0.00000095367431640625

查看评论中的结果。结果从调试器的手表复制。
产生这种效果的数字比数据类型的极限小得多,所以它不能是溢出(我猜!)。



<更令人感兴趣的是,可以有两个等于的十进制值(在上面的代码示例中,参见dcm和dcm2,deltaDcm等于零),导致转换时不同的双重值。 (在代码中,dbl和dbl2,它们有一个非零的deltaDbl)



我想这应该是有点与位在两种数据类型中表示数字,但无法弄清楚什么!而且,我需要知道如何使转换成为我需要的方式。 (如dcm2 - > dbl2)

解决方案

有趣的是 - 虽然我一般不信任正常写出浮点值的方法您对确切的结果感兴趣。



这是一个稍微简单的演示,使用 DoubleConverter.cs ,我曾经使用过几次。

 使用系统; 

class Test
{
static void Main()
{
decimal dcm1 = 8224055000.0000000000m;
十进制dcm2 = 8224055000m;
double dbl1 =(double)dcm1;
double dbl2 =(double)dcm2;

Console.WriteLine(DoubleConverter.ToExactString(dbl1));
Console.WriteLine(DoubleConverter.ToExactString(dbl2));
}
}

结果:

  8224055000.00000095367431640625 
8224055000

现在的问题是为什么原始值(8224055000.0000000000)是一个整数,并且可以直接表示为 double - 最终得到额外的数据。我强烈怀疑是由于怪癖在用于从 decimal 转换为 double 的算法中,但不幸的是,



它也违反C#规范的第6.2.1节:


对于从小数到浮点或双精度的转换,十进制值舍入为
最近的double或float值。虽然这种转换可能会失去精度,但它从不会导致
抛出异常。


最近的双重值是显然只是8224055000 ...所以这是一个bug的IMO。这不是我期望在很短的时间内修复的。 (它在.NET 4.0b1中给出相同的结果。)



为了避免错误,您可能希望首先对小数值进行规范化,有效地删除小数点后的额外0。这有点棘手,因为它涉及96位整数运算 - .NET 4.0 BigInteger 类可能会使它更容易,但这可能不是您的选择。 p>

Summary of the problem:

For some decimal values, when we convert the type from decimal to double, a small fraction is added to the result.

What makes it worse, is that there can be two "equal" decimal values that result in different double values when converted.

Code sample:

decimal dcm = 8224055000.0000000000m;  // dcm = 8224055000
double dbl = Convert.ToDouble(dcm);    // dbl = 8224055000.000001

decimal dcm2 = Convert.ToDecimal(dbl); // dcm2 = 8224055000
double dbl2 = Convert.ToDouble(dcm2);  // dbl2 = 8224055000.0

decimal deltaDcm = dcm2 - dcm;         // deltaDcm = 0
double deltaDbl = dbl2 - dbl;          // deltaDbl = -0.00000095367431640625

Look at the results in the comments. Results are copied from debugger's watch. The numbers that produce this effect have far less decimal digits than the limit of the data types, so it can't be an overflow (I guess!).

What makes it much more interesting is that there can be two equal decimal values (in the code sample above, see "dcm" and "dcm2", with "deltaDcm" equal to zero) resulting in different double values when converted. (In the code, "dbl" and "dbl2", which have a non-zero "deltaDbl")

I guess it should be something related to difference in the bitwise representation of the numbers in the two data types, but can't figure out what! And I need to know what to do to make the conversion the way I need it to be. (like dcm2 -> dbl2)

解决方案

Interesting - although I generally don't trust normal ways of writing out floating point values when you're interested in the exact results.

Here's a slightly simpler demonstration, using DoubleConverter.cs which I've used a few times before.

using System;

class Test
{
    static void Main()
    {
        decimal dcm1 = 8224055000.0000000000m;
        decimal dcm2 = 8224055000m;
        double dbl1 = (double) dcm1;
        double dbl2 = (double) dcm2;

        Console.WriteLine(DoubleConverter.ToExactString(dbl1));
        Console.WriteLine(DoubleConverter.ToExactString(dbl2));
    }
}

Results:

8224055000.00000095367431640625
8224055000

Now the question is why the original value (8224055000.0000000000) which is an integer - and exactly representable as a double - ends up with extra data in. I strongly suspect it's due to quirks in the algorithm used to convert from decimal to double, but it's unfortunate.

It also violates section 6.2.1 of the C# spec:

For a conversion from decimal to float or double, the decimal value is rounded to the nearest double or float value. While this conversion may lose precision, it never causes an exception to be thrown.

The "nearest double value" is clearly just 8224055000... so this is a bug IMO. It's not one I'd expect to get fixed any time soon though. (It gives the same results in .NET 4.0b1 by the way.)

To avoid the bug, you probably want to normalize the decimal value first, effectively "removing" the extra 0s after the decimal point. This is somewhat tricky as it involves 96-bit integer arithmetic - the .NET 4.0 BigInteger class may well make it easier, but that may not be an option for you.

这篇关于在C#中将十进制转换为双数可导致差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆