此操作返回0:
string value = “0.01”;
float convertedValue = float.Parse(value);
return (int)(convertedValue * 100.0f);
Run Code Online (Sandbox Code Playgroud)
但是这个操作返回1:
string value = “0.01”;
float convertedValue = float.Parse(value) * 100.0f;
return (int)(convertedValue);
Run Code Online (Sandbox Code Playgroud)
因为convertedValue是一个浮点数,它在括号内*100f它不应该被视为浮点运算吗?
Ken*_*rey 19
两者之间的区别在于编译器优化浮点运算的方式.让我解释.
string value = "0.01";
float convertedValue = float.Parse(value);
return (int)(convertedValue * 100.0f);
Run Code Online (Sandbox Code Playgroud)
在此示例中,该值被解析为80位浮点数,以便在计算机的内部浮点地牢中使用.然后将其转换为32位浮点数以存储在convertedValue变量中.这导致该值四舍五入到似乎小于0.01的数字.然后将其转换回80位浮点数并乘以100,将舍入误差增加100倍.然后它被转换为32位int.这会导致浮动被截断,并且因为它实际上略小于1,所以int转换返回0.
string value = "0.01";
float convertedValue = float.Parse(value) * 100.0f;
return (int)(convertedValue);
Run Code Online (Sandbox Code Playgroud)
在此示例中,该值再次被解析为80位浮点数.然后在它转换为32位浮点数之前再乘以100.这意味着舍入误差非常小,以至于当它被转换为32位浮点数进行存储时convertedValue,它将精确地舍入为1.然后当它转换为int时,得到1.
主要思想是计算机使用高精度浮点数进行计算,然后在将值存储在变量中时对其进行舍入.使用浮点数的分配越多,舍入错误累积的越多.
Cod*_*aos 14
请阅读浮点介绍.这是典型的浮点问题.二进制浮点不能完全表示0.01.
0.01 * 100 约为1.
如果它恰好被四舍五入到0.999...你得到0,如果它四舍五入1.000...你得到1.你得到的那个是未定义的.
每次遇到类似的表达式(甚至不同的上下文中的相同表达式)时,jit编译器都不需要以相同的方式进行舍入.特别是它可以随时使用更高的精度,但如果它认为这是一个好主意,可以降级到32位浮点数.
一个有趣的观点是显式转换float(即使您已经有类型的表达式float).这会强制JITer将精度降低到该点的32位浮点数.但确切的舍入仍未定义.
由于舍入未定义,因此可以在.net版本,调试/发布版本,调试器的存在(以及可能的月相:P)之间变化.
浮点数(静态,数组元素和类的字段)的存储位置具有固定大小.支持的存储大小为float32和float64.其他地方(在评估堆栈上,作为参数,作为返回类型和作为局部变量)浮点数使用内部浮点类型表示.
当内部表示具有比其标称类型更大的范围和/或精度的浮点值被放入存储位置时,它将自动强制转换为存储位置的类型.这可能涉及精度损失或创建超出范围的值(NaN,+无穷大或无穷大).但是,如果在未经修改的情况下从存储位置重新加载该值,则该值可能会保留在内部表示中以供将来使用. 编译器有责任确保保留值在后续加载时仍然有效,同时考虑到别名和其他执行线程的影响(参见内存模型(第12.6节)).但是,在执行显式转换(conv.r4或conv.r8)之后,不允许进行额外精度的自由,此时内部表示必须在关联类型中准确表示.
您的具体问题可以通过使用来解决Decimal,但类似的问题3*(1/3f)将无法解决,因为Decimal不能完全代表三分之一.