Для обозначения операций сложения, вычитания, умножения и деления в
языке Java используются обычные арифметические операторы + - * /.
Оператор / обозначает целочисленное деление, если оба его аргумента
являются целыми числами. В противном случае этот оператор обозначает
деление чисел с плавающей точкой. Остаток от деления целых чисел (т.е.
функция mod) обозначается символом %. Например, 15/2 равно 7, 15%2 равно 1, а 15 . 0/2 равно 7 . 5.
Заметим, что целочисленное деление на 0 возбуждает исключительную
ситуацию, в то время как результатом деления на 0 чисел с плавающей
точкой является бесконечность или NaN.
Арифметические операторы можно использовать для инициализации переменных.
int n = 5; int а = 2 * n; // Значение переменной а равно 10.
В операторах присваивания удобно использовать сокращенные бинарные арифметические операторы.
Например, оператор х + = 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 будут выполнять точные операции над числами с плавающей точкой.
Детали выполнения этих операций тесно связаны с особенностями работы
процессоров Intel. По умолчанию промежуточные результаты могут
использовать расширенный показатель, но не расширенную мантиссу.
(Микросхемы компании Intel поддерживают округление мантиссы без потери
производительности.) Следовательно, единственное различие между
вычислениями по умолчанию и точными вычислениями состоит в том, что
точные вычисления могут приводить к переполнению, а вычисления по
умолчанию — нет.
Если при чтении этого замечания ваш взгляд потускнел, не волнуйтесь.
Для большинства программистов этот вопрос совершенно не важен.
Переполнение при вычислениях чисел с плавающей точкой в большинстве
случаев не возникает. В этой книге мы не будем использовать ключевое
слово strictfp.
Операторы инкрементации и декрементации
Программисты, конечно, знают, что одной из наиболее распространенных
операций с числовыми переменными является добавление или вычитание
единицы. В языке Java, как и в языках С и C++, есть операторы
инкрементации и декрементации: оператор х++ добавляет единицу к
текущему значению переменной х, а оператор х- - вычитает из него
единицу.
Например, код int n = 12; n++; делает значение переменной n равным 13.
Поскольку эти операторы изменяют значение переменной, их нельзя
применять к самим числам. Например, оператор 4++ является недопустимым.
Существует два вида этих операторов. Выше показана "постфиксная"
форма оператора, в которой символы операции размещаются после
операнда. Есть и "префиксная" форма— ++n. Оба этих оператора
увеличивают значение переменной на единицу. Разница между ними
проявляется, только когда эти операторы используются внутри выражений.
Префиксная форма оператора инкрементации сначала добавляет единицу к
значению переменной, в то время как постфиксная форма использует
старое значение этой переменной.
int m = 7; int n = 7; int а = 2 * ++m; // Теперь значение а равно 16, a m — 8. int b = 2 * n++; // Теперь значение b равно 14, a n — 8.
Мы не рекомендуем использовать оператор инкрементации ++ внутри
выражений, поскольку это зачастую приводит к запутанному коду и
досадным ошибкам.
(Поскольку именно оператор ++ дал имя языку C++, это послужило
поводом к первой шутке о нем. Недоброжелатели указывают, что даже имя
этого языка содержит в себе ошибку: "Кроме всего прочего, этот язык
следовало бы назвать ++С, потому что мы хотим использовать этот язык
только после его улучшения".)
|