可能损失精度的不同行为
2022-09-01 10:57:45
在Java中,当你这样做
int b = 0;
b = b + 1.0;
您可能会丢失精度误差。但是,如果您这样做,为什么会这样
int b = 0;
b += 1.0;
没有任何错误?
在Java中,当你这样做
int b = 0;
b = b + 1.0;
您可能会丢失精度误差。但是,如果您这样做,为什么会这样
int b = 0;
b += 1.0;
没有任何错误?
那是因为 等效于 .缩小基元转换 (JLS 5.1.3) 隐藏在复合赋值操作中。b += 1.0;
b = (int) ((b) + (1.0));
形式为 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 Puzzlers一书。以下是本书的一些摘录(为简洁起见,略作编辑):
许多程序员认为这只是 .这并不完全正确:如果结果的类型比变量的类型宽,则复合赋值运算符将执行静默的缩小基元转换。
x += i;
x = x + i;
为避免令人不快的意外,请勿对 类型为 、 或 的变量使用复合赋值运算符。对类型的变量使用复合赋值运算符时,请确保右侧的表达式不是 、 或 类型。对类型的变量使用复合赋值运算符时,请确保右侧的表达式不是类型。这些规则足以防止编译器生成危险的缩小强制转换。
byte
short
char
int
long
float
double
float
double
对于语言设计人员来说,复合赋值运算符生成不可见的强制转换可能是一个错误;变量的类型比计算结果窄的复合赋值可能是非法的。
最后一段值得注意:C#在这方面要严格得多(参见C#语言规范7.13.2复合赋值)。