Изменяющееся поведение для возможной потери точности - PullRequest
30 голосов
/ 23 апреля 2010

В Java, когда вы делаете

int b = 0;
b = b + 1.0;

Вы получаете возможную потерю точности ошибки. Но почему это так, если вы делаете

int b = 0;
b += 1.0;

Нет ошибки?

1 Ответ

34 голосов
/ 23 апреля 2010

Это потому, что b += 1.0; эквивалентно b = (int) ((b) + (1.0));. сужающее примитивное преобразование (JLS 5.1.3) скрыто в составной операции присваивания.

JLS 15.26.2 Составные операторы присваивания (JLS Third Edition):

Составное выражение присваивания в форме 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: Твидледум из замечательной книги Java Puzzlers .Вот некоторые выдержки из книги (слегка отредактированные для краткости):

Многие программисты считают, что x += i; - это просто сокращение для x = x + i;.Это не совсем верно: если тип результата шире, чем у переменной, составной оператор присваивания выполняет тихое сужающее примитивное преобразование.

Чтобы избежать неприятных сюрпризов, не использует составноеоператоры присваивания переменных типа byte, short или char.При использовании составных операторов присваивания для переменных типа int убедитесь, что выражение справа не имеет типа long, float или double.При использовании составных операторов присваивания для переменных типа float убедитесь, что выражение в правой части не имеет типа double.Эти правила достаточны для того, чтобы компилятор не генерировал опасные сужающие приведения.

Для разработчиков языков, вероятно, ошибочно для составных операторов присваивания создавать невидимые приведения;составные присваивания, в которых переменная имеет более узкий тип, чем результат вычисления, вероятно, должны быть недопустимыми.

Последний абзац стоит отметить: C # намного более строг в этом отношении (см. Спецификация языка C # 7.13.2 Составное присваивание ).

...