.NET C# float.parse与double.parse有不同的结果

.NET C# float.Parse returning different result to double.Parse

本问题已经有最佳答案,请猛点这里访问。

一些更有经验的人能解释我今天发现的这个奇怪的错误吗?当我用我的C脚本加载表数据时,我得到了奇怪的数量。

事实证明,问题是类似功能的不同输出:

1
2
3
4
5
6
string amount_in_string ="1234567,15";
double amount_in_double = double.Parse(amount_in_string);
float amount_in_float = float.Parse(amount_in_string);

//amount_in_double = 1234567.15;
//amount_in_float = 1234567.13;

当float和double是相似的类型(浮点)时,为什么会得到如此不同的结果呢?像这样的小数目的精密度能有区别吗?


当"1234567.15"转换为double时,结果是double中可表示的最接近的值,即1234567.149999999906867742538421484375。尽管您在问题中报告的值为1234567.15,但实际值为1234567.149999999906867742538421484375。当数值以有限的小数位数显示时,将显示"1234567.15"。

当"1234567.15"转换为float时,结果是float中可表示的最接近值,即1234567.125。尽管您报告的值是1234567.13,但实际值是1234567.125。当数值以有限的小数位数显示时,可能会显示"1234567.13"。

观察1234567超过1048576,即220。用于float的32位浮点格式使用24位作为有效位(数字的小数部分)。如果有效位的高位代表220,低位代表220?23=2?3=?。这就是为什么您看到"1234567.15"转换为四舍五入到最接近的八分之一的值。


浮点数从不精确,它们是数字的表示。一个常用的例子是

1/3 + 1/3 = 2/3

…所以浮点数的答案,.33333 + .33333不是2/3rds,而是.66666

长话短说,你拿的更精确的分数,不能转换成精确的二进制数字,总是会有一个舍入数。越精确,就越有可能出现舍入误差。

请记住,如果您使用多个不同的分数,您甚至可以有多个不同的舍入错误,这些错误会使数字意外地正确,甚至进一步偏离。


你可以看到结果(这里也存在文化问题)

https://dotnetfidle.net/lnv1q7

1
2
3
4
5
6
7
8
    string amount_in_string ="1234567,15"; // NOTE THE COMMA in original
    string amount_in_string ="1234567.15"; //my correct culture
    double amount_in_double = double.Parse(amount_in_string);
    float amount_in_float = float.Parse(amount_in_string);

    Console.WriteLine(amount_in_string);
    Console.WriteLine(amount_in_double);
    Console.WriteLine(amount_in_float);

结果(在不正确的区域性中分析!)

1
2
3
1234567,15
123456715
1.234567E+08

结果(在正确的区域性中分析!)

1
2
3
1234567.15
1234567.15
1234567

另一个用float证明精度损失的方法。

1
2
3
4
float flt = 1F/3;
double dbl = 1D/3;
decimal dcm = 1M/3;
Console.WriteLine("float: {0} double: {1} decimal: {2}", flt, dbl, dcm);

结果

1
2
3
float: 0.3333333  
double: 0.333333333333333  
decimal: 0.3333333333333333333333333333

浮点数只能在某些精度损失不是非常有价值的情况下使用。这是因为浮点数是32位,而十进制数是128位。浮动主要用于像素坐标,精度的损失并不重要,因为消费者可以以任何方式将位置转换为更精确的坐标。

在.NET中,除非您不关心(某些)精度的损失,否则应该积极避免浮动。这可能永远不会是一天(除非你写游戏)

这就是银行业问题的根源所在,在每笔交易中损失了100美分/美分,看似看不见,但相当于大量的"失踪"资金。

使用小数