В 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;
Нет ошибок?
Это потому, что b += 1.0;
эквивалентно b = (int) ((b) + (1.0));
. сужение примитивного преобразования (JLS 5.1.3) скрывается в составной операции присваивания.
Выражение составного присваивания формы 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
. Эти правила достаточны, чтобы компилятор не создавал опасные сужающие приведения.Для разработчиков языка, вероятно, ошибкой для сложных операторов присваивания создавать невидимые роли; составные назначения, где переменная имеет более узкий тип, чем результат вычисления, вероятно, должны быть незаконными.
Следует отметить последний абзац: С# гораздо более строг в этом отношении (см. Спецификация языка С# 7.13.2 Составное назначение).
Может быть, и, возможно, это потому, что в первом он преобразует b в плавающую точку, а затем добавляет, а затем преобразует его обратно в целое число?
И последним он может преобразовывать 1.0 в integer вместо этого и делать целое дополнение?
Только предположение.