Для обозначения арифметических операций сложения, вычитания, умножения и деления в Java используются обычные знаки подобных операций: + - * и / соответственно. Операция / обозначает целочисленное деление, если оба ее аргумента являются целыми числами. В противном случае эта операция обозначает деление чисел с плавающей точкой. Остаток от деления целых чисел обозначается символом %. Например, 15/2 равно 7, 15%2 равно 1, а 15.0/2 — 7.5.
Заметим, что в результате целочисленного деления на нуль генерируется исключение, в то время как результатом деления на нуль чисел с плавающей точкой является бесконечность или NaN.
В Java предусмотрена сокращенная запись бинарных арифметических операций (т.е. операций, предполагающих два операнда). Например, оператор
х += 4; равнозначен оператору
х = х + 4;
(В сокращенной записи символ арифметической операции, например * или %, размещается перед знаком равенства, например *= или %=.)
НА ЗАМЕТКУ! Одной из заявленных целей языка Java является переносимость. Вычисления должны приводить к одинаковому результату, независимо от того, какая виртуальная машина их выполняет. Для арифметических операций над числами с плавающей точкой соблюдение этого требования неожиданно оказалось непростой задачей. Для хранения числовых значений типа double используются 64 бита, но в некоторых процессорах применяются 80-разрядные регистры с плавающей точкой. Эти регистры обеспечивают дополнительную точность на промежуточных этапах вычисления. Рассмотрим в качестве примера следующее выражение:
double w = х * у / z;
Многие процессоры компании Intel вычисляют выражение х * у и сохраняют этот промежуточный результат в 80-разрядном регистре, затем делят его на значение переменной z и, наконец, округляют результат до 64 бит. Подобным образом можно повысить точность вычислений, избежав переполнения. Но этот результат может оказаться иным, если в процессе всех вычислений используется 64-разрядный процессор. По этой причине в первоначальном описании виртуальной машины Java указывалось, что все промежуточные вычисления должны округляться. Это вызвало протест многих специалистов. Округление не только может привести к переполнению. Вычисления при этом происходят медленнее, поскольку операции округления отнимают некоторое время. По этой причине язык Java был усовершенствован таким образом, чтобы распознавать случаи конфликтующих требований для достижения оптимальной производительности и точной воспроизводимости результатов. По умолчанию при промежуточных вычислениях в виртуальной машине может использоваться повышенная точность. Но в методах, помеченных ключевым словом strictfp, должны применяться точные операции над числами с плавающей точкой, гарантирующие воспроизводимость результатов. Например, метод main () можно записать так:
public static strictfp void main(String[] args)
В этом случае все команды в теле метода main () будут выполнять точные операции над числами с плавающей точкой. А если пометить ключевым словом strictfp класс, то во всех его методах должны выполняться точные операции с плавающей точкой.
Многое при подобных вычислениях зависит от особенностей работы процессоров Intel. По умолчанию в промежуточных результатах может использоваться расширенный показатель степени, но не расширенная мантисса. (Процессоры компании Intel поддерживают округление мантиссы без потери производительности.) Следовательно, вычисления по умолчанию отличаются от точных вычислений лишь тем, что в последнем случае возможно переполнение.
Если сказанное выше кажется вам слишком сложным, не отчаивайтесь. Переполнение при вычислениях с плавающей точкой, как правило, не возникает. А в примерах, рассматриваемых в этой книге, ключевое слово strictfp использоваться не будет.
Операции инкрементирования и декрементирования
Программисты, конечно, знают, что одной из самых распространенных операций с числовыми переменными является добавление или вычитание единицы. В Java, как и в С и С++, для этой цели предусмотрены операции инкрементирования и декрементирования. Так, в результате вычисления оператора п++ к текущему значению переменной п прибавляется единица, а оператор п— уменьшает ее значение на единицу. После выполнения приведенного ниже фрагмента кода значение переменной п становится равным 13.
int n = 12; n++;
Операции C++ и — изменяют значение переменной, и поэтому их нельзя применять к самим числам. Например, выражение 4++ считается недопустимым.
Существуют два вида операций инкрементирования и декрементирования. Выше продемонстрирована постфиксная форма, в которой символы операции размещаются после операнда. Но есть и префиксная форма: ++п. Обе эти операции изменяют значение переменной на единицу. Их отличие проявляется только тогда, когда эти операции присутствуют в выражениях. В префиксной форме сначала изменяется значение переменной, и для дальнейших вычислений уже используется новое значение, а в постфиксной форме используется старое значение этой переменной, и лишь после данной операции оно изменяется, как показано в приведенных ниже примерах.
int m = 7; int n = 7;
int a = 2 * ++m; // теперь значение а равно 16, am равно 8 int b = 2 * n++; // теперь значение b равно 14, a n равно 8
Пользоваться операциями инкрементирования и декрементирования в выражениях не рекомендуется, поскольку это зачастую запутывает код и приводит к досадным ошибкам.
(Именно операция С++ дала название языку С++, что и послужило поводом к первой шутке о нем. Недоброжелатели отмечают, что даже имя этого языка содержит в себе ошибку: "Язык следовало бы назвать ++С, потому что мы хотели бы пользоваться им только после его улучшения".) |