Различное поведение для возможной потери точности
-
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 Third Edition):
Выражение составного назначения формы E1 OP = E2 эквивалентно E1 = (T) ((E1) OP (E2)), куда Т это тип 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!
Стоит отметить, что неявный литой в составных заданиях является предметом Puzzle 9: TweeDledum от прекрасной книги Java Pazzlers. Отказ Вот несколько выдержка из книги (слегка отредактирована для краткости):
Многие программисты думают, что
x += i;
это просто сокращение дляx = x + i;
. Отказ Это не совсем верно: если тип результата шире, чем у переменной, оператор составного назначения выполняет бесшумный сужение примитивного преобразования.Чтобы избежать неприятных сюрпризов, Не используйте комбинированные операторы назначения на переменные типа
byte
,short
, илиchar
. Отказ При использовании операторов составного назначения на переменные типаint
, убедитесь, что выражение на правой стороне не типаlong
,float
, илиdouble
. Отказ При использовании операторов составного назначения на переменные типаfloat
, убедитесь, что выражение на правой стороне не типаdouble
. Отказ Эти правила достаточно, чтобы помешать компилятору генерировать опасные суженные каста.Для дизайнеров языков, вероятно, является ошибкой для операторов составных присваиваний для генерации невидимых отливок; Составные задания, в которых переменная имеет более узкий тип, чем результат вычислений, вероятно, должен быть незаконным.
Последний абзац стоит отметить: C # намного более строгим в этом отношении (см. C # Спецификация языка 7.13.2 Составное назначение).