我遇到了麻烦:
int q = 150;
float s = 0.7f;
float z = q*s;
int z1 = (int) (q*s);
int z2 = (int) z;
这导致
z1
int
的值为104
z2
int
的值为105
任何人都能解释一下吗?我不明白这些结果。
为了避免关闭,我(RenéVogt)添加了这些信息:
q*s
会产生float
的值105.0f
(或者104.999999
,但字符串表示最终为105
)。z
是float
105
问题现在是,为什么(int)z
会产生105
,但(int)(q*s)
会产生104
?我可以在我的机器上重现这个(i7,Win10,VS2015,.NET4.6.1)
和IL代码:
// Initialisation
// q = 150
ldc.i4 0x96
stloc.0
// s = 0.7f
ldc.r4 0.69999999
stloc.1
// calculating z
ldloc.0 // load q
conv.r4 // convert to float
ldloc.1 // load s
mul // q*s
stloc.2 // store float result to z
// calulating z1
ldloc.0 // load q
conv.r4 // convert to float
ldloc.1 // load s
mul // q*s
conv.i4 // convert to int
stloc.3 // store int result in z1 => 104!
// calculating z2
ldloc.2 // loading float z
conv.i4 // converting to int
stloc.s // write to z2 (last local variable -> "s" as stack address)
// => 105
所以我认为z1
和z2
之间的唯一区别是,对于z2
,中间float
结果会从寄存器写入局部变量({{1}存储位置。但是这会对结果产生怎样的影响?
答案 0 :(得分:6)
数字0.7
无法用float
完全表示,而s
的值更接近0.699999988079071044921875
。
int
的{{1}}值将转换为q
,因为这可以直接表示为float
。
如果将两者相乘,则不会完全得到150
:
q = 150
s = 0.699999988079071044921875
q * s = 104.999998211861
现在参考CLI Spec (ECMA-335)§12.1.3中的相关部分:
当内部表示具有比其标称类型更大的范围和/或精度的浮点值被放入存储位置时,它将自动强制转换为存储位置的类型。这可能涉及 精度损失或创建超出范围的值(NaN,+无穷大或无穷大)。但是,如果在未经修改的情况下从存储位置重新加载该值,则该值可能会保留在内部表示中以供将来使用。编译器有责任确保保留值在后续加载时仍然有效,同时考虑到别名和其他执行线程的影响(请参阅内存模型(第12.6节))。但是,在执行显式转换(conv.r4或conv.r8)之后,不允许进行额外精度的自由,此时内部表示必须在关联类型中准确表示。
因此105
会产生一个精度高于q * s
可以处理的值。将其直接存储到float
:
int
该值永远不会强制转换为var z1 = (int)(q * s);
类型,而是直接转换为float
,从而被截断为104.
在所有其他示例中,值被转换为或存储在int
中,因此转换为最接近的float
值,即105.
答案 1 :(得分:0)
我猜你在x64上运行这个?
因此我假设乘法发生为double
。但是,double
会被退回到float
进行存储(因此例如为105.000000001)
mul // q*s
stloc.2 // store float result to z (stored as float)
当直接相乘并转换为int时,乘法发生为double
,可以表示为例如int
。 104.999999,并截断为mul // q*s
conv.i4 // convert to int
(104)
temp = {"1": "hello", "2": "goodbye", "3": "hello", "4": "goodbye", "5": "hi"}
查看此答案:Strange behaviour when casting a float to int in C#
编辑:在第一个实例中,编译器没有选项,只能使用浮点运算,因为它需要将它存储回浮点数。但是对于直接转换为int的第二个选项,它可以根据链接的答案使用更高精度的算法。
答案 2 :(得分:-1)
当转换为二进制到十进制时,你不能完全表示相同的数字(rember binary只有2个charaml,其中deciaml有10个)。
十进制当量试图写在纸上1/3 = 0.3333333333333 ......(无穷大!)
由于浮点数不能代表递归数字,因此最终只能存储0.333333而非0.333333333333 ....这会导致精度下降....(简化示例)这对于低精度应用程序和性能优势非常有用(当您考虑时)数百万计算)
相反,你应该使用Decimal或Double类型,因为它们能够使用科学符号表示来存储数字,这种表示法不会轻易地降低精度。
我遇到过的最好的解释是:https://www.youtube.com/watch?v=PZRI1IfStY0