变化的行为可能导致精度损失

在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!

值得注意的是,复合赋值中的隐式转换是来自精彩书籍“Java Puzzlers”中的Puzzle 9:Tweedledum的主题。 以下是本书的一些摘录(为简洁起见,稍作编辑):

许多程序员认为x += i; 只是x = x + i;的简写x = x + i; 。 这不太正确:如果结果的类型比变量的类型宽,则复合赋值运算符执行静默缩小基元转换。

为避免不愉快的意外, 不要对 byteshortchar 类型的变量使用复合赋值操作符 。 在int类型的变量上使用复合赋值运算符时,请确保右侧的表达式不是long类型, float类型或double类型的类型。 在float类型的变量上使用复合赋值运算符时,请确保右侧的表达式不是double类型。 这些规则足以防止编译器产生危险的缩小转换。

对于语言设计师来说,复合赋值操作符产生不可见的演员可能是一个错误; 其中变量的类型比计算结果更窄的复合赋值应该是非法的。

最后一段值得注意:C#在这方面比较严格(请参阅C#语言规范7.13.2复合赋值)。


也许,也许是因为在前者将b转换为浮点,然后添加,然后将其转换回整数?

而后者可能会将1.0转换为整数,并进行整数加法?

只是一个猜测。

链接地址: http://www.djcxy.com/p/73635.html

上一篇: Varying behavior for possible loss of precision

下一篇: Does Java compiler recognize byte and short as literals?