Для каких целей Java имеет тип примитивов с плавающей точкой? - PullRequest
20 голосов
/ 29 апреля 2010

Можете ли вы помочь мне прояснить использование примитива float в Java?

Насколько я понимаю, преобразование значения с плавающей запятой в удвоение и наоборот может быть проблематичным. Я читал (довольно давно и не уверен, что с новыми JVM это действительно так), что производительность float намного хуже, чем у double. И, конечно, поплавки имеют меньшую точность, чем двойные.

Я также помню, что когда я работал с AWT и Swing, у меня были некоторые проблемы с использованием float или double (например, с использованием Point2D.Float или Point2D.Double).

Итак, я вижу только 2 преимущества float по сравнению с double:

  1. Требуется всего 4 байта, в то время как double нужно 8 байтов

  2. Модель памяти Java (JMM) гарантирует, что операция присваивания является атомарной с переменными с плавающей точкой, в то время как она не атомарна с двойными.

Есть ли другие случаи, когда float лучше, чем double? Вы используете float в своих приложениях?

Ответы [ 5 ]

15 голосов
/ 29 апреля 2010

Причина включения типа с плавающей запятой в некоторой степени историческая: он представляет стандартное представление IEEE с плавающей запятой со времен, когда сокращение на 4 байта от размера числа с плавающей запятой в обмен на чрезвычайно низкую точность было компромиссом, который стоит сделать .

В настоящее время использование поплавка довольно ограничено. Но, например, наличие типа данных может облегчить написание кода, который требует взаимодействия со старыми системами, которые используют float.

Что касается производительности, я думаю, что float и double по существу идентичны , за исключением производительности делений . Как правило, независимо от того, что вы используете, процессор конвертирует в свой внутренний формат, выполняет вычисления, а затем снова выполняет обратное преобразование, и фактический расчет фактически занимает фиксированное время. В случае делений, по крайней мере, на процессорах Intel, насколько я помню, время, затрачиваемое на деление, обычно составляет один тактовый цикл на 2 бита точности, так что если вы используете float или double, то разница будет иметь место.

Если у вас действительно нет веской причины для его использования в новом коде, я бы вообще избегал 'float'.

7 голосов
/ 29 апреля 2010

Эти две причины, которые вы только что дали, огромны.

Если у вас есть 3D-объем 1 к 1 на 64, а затем у вас есть много временных точек этих данных, а затем вы хотите снять фильм с проекциями максимальной интенсивности, то факт, что float в два раза меньше двойного, может быть Разница между быстрым завершением и порчей, потому что у вас закончилась память.

Атомность также огромна, с точки зрения многопоточности.

Всегда будет компромисс между скоростью / производительностью и точностью. Если у вас есть число меньше 2 ^ 31 и целое число, то целое число всегда будет лучшим представлением этого числа, чем число с плавающей точкой, просто из-за потери точности. Вам придется оценить свои потребности и использовать соответствующие типы для ваших проблем.

3 голосов
/ 29 апреля 2010

Я думаю, что вы прибили это, когда упомянули о хранении, с поплавками, равными половине размера.

Использование чисел с плавающей запятой может показывать улучшенную производительность по сравнению с удвоениями для приложений, обрабатывающих большие массивы чисел с плавающей запятой, так что пропускная способность памяти является ограничивающим фактором. Переключившись на float[] с double[] и сократив вдвое размер данных, вы эффективно удваиваете пропускную способность, потому что за один раз можно получить вдвое больше значений. Хотя процессору требуется немного больше для преобразования числа с плавающей точкой в ​​двойное, это происходит параллельно с извлечением памяти, причем извлечение занимает больше времени.

Для некоторых приложений потеря точности может стоить тратить на увеличение производительности. Тогда снова ...: -)

2 голосов
/ 29 апреля 2010

Это правда, что в некоторых случаях удваивание может выполняться быстрее, чем с плавающей точкой. Однако для этого необходимо, чтобы все помещалось в L1-кэш. С плавающими вы можете иметь вдвое больше в строке кэша. Это может заставить некоторые программы работать почти вдвое быстрее.

Инструкции SSE также могут работать с 4 поплавками параллельно вместо 2, но я сомневаюсь, что JIT фактически использует их. Хотя я могу ошибаться.

2 голосов
/ 29 апреля 2010

Так что да, преимущества поплавков:

  1. Требуется только 4 байта
  2. Атомное присвоение
  3. Арифметика должна быть быстрее, особенно на 32-битных архитектурах, поскольку существуют специальные плавающие байтовые коды.

Способы их устранения при использовании double:

  1. Купите больше оперативной памяти, это действительно дешево.
  2. Используйте изменчивые двойные, если вам нужно атомарное присваивание.
  3. Проводите тесты, проверяйте производительность каждого из них, если один действительно быстрее, с этим ничего не поделаешь.

Кто-то еще упомянул, что это похоже на аргумент short против int, но это не так. Все целочисленные типы (включая логические), кроме long, хранятся как 4-байтовые целые в модели памяти Java, если не хранятся в массиве.

...