我遇到了问题:
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
是 105
的 float
现在的问题是,为什么(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
结果得到从寄存器写入局部变量的(z
)存储位置。但这对结果有何影响?
数字 0.7
不能用 float
精确表示,s
的值更接近 0.699999988079071044921875
.
q
的 int
值将转换为 float
,因为这可以直接表示为 150
.
如果将两者相乘,您将不会得到 105
:
q = 150
s = 0.699999988079071044921875
q * s = 104.999998211861
现在引用CLI Spec (ECMA-335)中的相关部分§12.1.3:
When a floating-point value whose internal representation has greater range and/or precision than its nominal type is put in a storage location, it is automatically coerced to the type of the storage location. This can involve
a loss of precision or the creation of an out-of-range value (NaN, +infinity, or -infinity). However, the value might be retained in the internal representation for future use, if it is reloaded from the storage location without having been modified. It is the responsibility of the compiler to ensure that the retained value is still valid at the time of a subsequent load, taking into account the effects of aliasing and other execution threads (see memory model (§12.6)). This freedom to carry extra precision is not permitted, however, following the execution of an explicit conversion (conv.r4 or conv.r8), at which time the internal representation must be exactly representable in the associated type.
因此 q * s
产生的值比 float
可以处理的精度更高。将其直接存储到 int
时:
var z1 = (int)(q * s);
该值永远不会被强制转换为 float
类型,而是直接转换为 int
并因此被截断为 104。
在所有其他示例中,值被转换为或存储在 float
中,因此转换为最接近的 float
值,即 105。