C# 为什么(int)(float.Parse(";0.04";,System.Globalization.CultureInfo.InvariantCulture)*100)等于3
我的老板今天报告了我的一个bug,因为每当他想要指定一个低于5%的值时,我的配置就会减少。我知道我可以在将数字转换为int之前对其进行四舍五入以解决问题,但我不明白为什么会出现这个问题 我有一个值为“0.04”的app.config文件和一个带有float属性的配置部分。读取节时,检索到的浮点值为0.04,这很好。我想把这个值放在一个windows窗体跟踪栏中,它接受一个整数值,所以我将我的值乘以100,并将其转换为int。出于某种原因,结果不是4,而是3。您可以这样测试它:C# 为什么(int)(float.Parse(";0.04";,System.Globalization.CultureInfo.InvariantCulture)*100)等于3,c#,.net,C#,.net,我的老板今天报告了我的一个bug,因为每当他想要指定一个低于5%的值时,我的配置就会减少。我知道我可以在将数字转换为int之前对其进行四舍五入以解决问题,但我不明白为什么会出现这个问题 我有一个值为“0.04”的app.config文件和一个带有float属性的配置部分。读取节时,检索到的浮点值为0.04,这很好。我想把这个值放在一个windows窗体跟踪栏中,它接受一个整数值,所以我将我的值乘以100,并将其转换为int。出于某种原因,结果不是4,而是3。您可以这样测试它: Console.W
Console.WriteLine((int)(float.Parse("0.04", System.Globalization.CultureInfo.InvariantCulture) * 100)); // 3
发生了什么?之所以发生这种情况,是因为浮点值实际上更像是0.039999999;因此,您正在将3.9999999999这样的值转换为int,这将产生3 您可以通过四舍五入解决此问题:
Console.WriteLine((int)Math.Round(float.Parse("0.04", System.Globalization.CultureInfo.InvariantCulture) * 100));
实际上不是4而是399999和很多其他数字。这样做:
(int)(float.Parse("0.04") * 100.0 + 0.5)
“强制转换为浮点”类似于一个楼层操作符,因为它不完全是4,所以它被截断为3。这是因为0.04不能精确表示为
浮点-
,也不能表示它乘以100的结果。结果稍微小于4,因此转换为int将截断它
基本上,如果您想使用十进制准确表示的数字,应该使用decimal
类型,而不是float
或double
。有关更多信息,请参阅我在和上的文章
编辑:事实上,这里发生了一些更有趣的事情。。。特别是,如果先将结果指定给局部变量,则会更改结果:
using System;
using System.Globalization;
class Test
{
static void Main()
{
// Assign first, then multiply and assign back, then print
float f = Foo();
f *= 100;
Console.WriteLine((int) f); // Prints 4
// Assign once, then multiply within the expression...
f = Foo();
Console.WriteLine((int) (f * 100)); // Prints 4
Console.WriteLine((int) (Foo() * 100)); // Prints 3
}
// No need to do parsing here. We just need to get the results from a method
static float Foo()
{
return 0.04f;
}
}
我不确定这里到底发生了什么,但0.04f的确切值是:
0.039999999105930328369140625
。。。因此,它不打印4是有意义的,这是可能的
如果用double
算法而不是float
执行100的乘法,我可以强制结果为3:
f = Foo();
Console.WriteLine((int) ((double)f * 100)); // Prints 3
。。。但我不清楚为什么在原始版本中会出现这种情况,因为
float.Parse
返回float
,而不是double
。猜测一下,结果保留在寄存器中,随后的乘法使用double
算法执行(根据规范这是有效的),但这肯定是一个惊人的差异。作为浮点0.04*100很可能表示为3.99999999999,而强制转换为int只会截断它,所以,这就是为什么你看到3要加上这个,如果你需要精度,并且你知道它只有2位小数,请使用十进制类型。阅读“每个计算机科学家都应该知道的关于浮点运算的知识”。是的,乔恩,我们知道,你的…;)“事实上,这里发生了更有趣的事情……仍在调查……”嗯?我看这里没什么特别的f*100
在我的系统上是4-8940697E-08
,显示为4
,因为默认的ToString()
转换忽略了最低有效位。而且舍入误差不会被抵消,因为一些中间结果保持为double/extended。中间结果可以保持比静态类型更高的精度。AFAIK分配给局部变量可能会改变,也可能不会改变。另一方面,显式强制转换为float
(即使类型已经是float
)将保证值降级为float
可以表示的值。@CodeInChaos:Yes。。。我相信结果将取决于CLR版本和CPU体系结构。@CodeInChaos分配给局部变量确实会改变这一点,因为它会导致编译器发出一条指令,将值截断到精确的浮点
。