不同的行为可能会丧失精度
-
01-10-2019 - |
题
在Java,当您这样做的时候
int b = 0;
b = b + 1.0;
您可能会损失精度错误。但是为什么如果你这样做
int b = 0;
b += 1.0;
没有任何错误?
解决方案
那是因为 b += 1.0;
等同于 b = (int) ((b) + (1.0));
. 。这 缩小原始转换(JLS 5.1.3) 隐藏在复合分配操作中。
JLS 15.26.2复合分配操作员 (JLS第三版):
表格的复合分配表达式 E1 OP = E2 等同于 E1 =(T)((E1)OP(E2)), , 在哪里 t 是 E1, , 除了那个 E1 仅评估一次。
例如,以下代码是正确的:
short x = 3; x += 4.6;
和结果
x
具有价值7
因为它等同于:short x = 3; x = (short)(x + 4.6);
这也解释了为什么以下代码编译:
byte b = 1;
int x = 5;
b += x; // compiles fine!
但这不是:
byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!
在这种情况下,您需要明确施放:
byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!
值得注意的是,复合作业中的隐式演员是 难题9:Tweedledum 从精彩的书中 Java难题. 。以下是这本书的摘录(简洁地编辑):
许多程序员认为
x += i;
只是一个速记x = x + i;
. 。这并不是很正确:如果结果的类型比变量的类型宽,则复合分配操作员会执行静默狭窄的原始转换。为了避免令人不愉快的惊喜, 请勿在类型变量上使用复合分配运算符
byte
,short
, , 或者char
. 。当在类型变量上使用复合分配运算符时int
, ,确保右侧的表达不为类型long
,float
, , 或者double
. 。当在类型变量上使用复合分配运算符时float
, ,确保右侧的表达不为类型double
. 。这些规则足以防止编译器产生危险的狭窄铸件。对于语言设计师来说,复合分配操作员生成隐形演员可能是一个错误。该变量具有比计算结果更窄类型的复合分配可能是非法的。
最后一段值得注意的是:C#在这方面更加严格(请参阅 C#语言规范7.13.2复合分配).
不隶属于 StackOverflow