Зачем вам нужен float / double?


29

Я смотрел http://www.joelonsoftware.com/items/2011/06/27.html и смеялся над шуткой Джона Скита о 0,3, а не 0,3. Лично у меня никогда не было проблем с плавающими / десятичными / двойными числами, но потом я помню, что я выучил 6502 очень рано и никогда не нуждался в плаваниях в большинстве моих программ. Единственный раз, когда я использовал это, было для графики и математики, где неточные числа были в порядке, и вывод был для экрана и не должен быть сохранен (в БД, файле) или зависеть от.

У меня вопрос, где вы обычно использовали числа с плавающей запятой / десятичные числа / двойные? Так что я знаю, чтобы не упустить эти ошибки. С деньгами я использую long и сохраняю значения с центом, для скорости объекта в игре я добавляю int и делю (или bithift) значение, чтобы узнать, нужно ли мне перемещать пиксель или нет. (Я сделал перемещение объекта за 6502 дня, у нас не было ни деления, ни плавания, но были смены).

Так что мне было в основном любопытно.


10
потому что очень важно, что проценты, которые я плачу по ипотеке, остаются 12,6, а в 13 лет просто потому, что 13 - это такое хорошее круглое число.
Чани

1
«Я выучил 6502 очень рано и никогда не нуждался в плавающих элементах в большинстве моих программ ... для скорости объекта я добавляю целые числа и делю значение, чтобы знать, перемещать пиксель или нет» Это очень необычные способы решения этих задач в современной практике, за исключением представления денег в виде длинных центов.
jprete

Хорошо, что компьютер понимает миллиценты.
Tylermac

1
Или, кроме того, зачем использовать десятичные дроби, когда мы можем использовать дроби?
Tylermac

6
@ Scrooge - по иронии судьбы, вы не можете представить 0,6 в плавающем
Мартин Беккет

Ответы:


28

Потому что они для большинства целей более точны, чем целые числа.

Теперь как это? «для скорости объекта в игре ...» это хороший пример для такого случая. Скажем, вам нужны очень быстрые объекты, например, пули. Чтобы иметь возможность описывать их движение с помощью целочисленных переменных скорости, вам необходимо убедиться, что скорости находятся в диапазоне целочисленных переменных, то есть вы не можете иметь произвольно точный растр.

Но тогда вы также можете описать некоторые очень медленные объекты, такие как часовая стрелка часов. Так как это примерно на 6 порядков медленнее, чем у объектов маркера, первые ld (10⁶) ≈ 20 бит равны нулю, что исключает short intтипы с самого начала. Хорошо, сегодня у нас есть longs везде, что оставляет нам все еще комфортные 12 бит. Но даже в этом случае тактовая частота будет с точностью до четырех знаков после запятой. Это не очень хорошие часы ... но это, конечно, хорошо для игры. Просто вы бы не хотели делать растр намного грубее, чем он есть.

... что приводит к проблемам, если однажды вы захотите представить новый, еще более быстрый тип объекта. Не осталось "запаса".

Что произойдет, если мы выберем тип float? Тот же самый размер 32 бита, но теперь у вас есть полные 24 бита точности для всех объектов. Это означает, что часы имеют достаточную точность, чтобы оставаться синхронизированными с точностью до секунды в течение многих лет. Пули не имеют более высокой точности, но в любом случае они «живут» лишь доли секунды, поэтому было бы совершенно бесполезно, если бы они имели. И у вас не будет никаких неприятностей, если вы хотите описать даже гораздо более быстрые объекты (почему бы не скорость света? Нет проблем) или гораздо более медленные. Вам, конечно, не понадобятся такие вещи в игре, но вы иногда делаете это в симуляторах физики.

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


Целые числа совершенно точны. Неточность зависит от неверного расчета.
fjdumont

15
Целые числа совершенно точны только тогда, когда вы используете их для фактического представления целых (ℤ) чисел. Представление чего-либо еще означает, действительно, неправильный расчет. В таком случае у вас есть две возможности: либо определить какой-то тип, который идеально подходит для чисел, которые вы на самом деле хотите представить. это возможно, например, Mathematica может это сделать. Но это очень сложно и дорого, и, как правило, не стоит усилий, потому что на самом деле вам не нужна идеальная точность. Но вам нужна хорошая точность, и именно здесь числа с плавающей точкой обычно работают лучше, чем целые числа.
оставил около

53

Вы используете их, когда описываете непрерывное значение, а не дискретное . Это не сложнее описать, чем это. Только не делайте ошибку, предполагая, что любое значение с десятичной точкой является непрерывным. Если все меняется сразу, например, добавляя копейки, это дискретно.


28

У вас действительно есть два вопроса здесь.

В любом случае, зачем кому-то нужна математика с плавающей точкой?

Как указывает Карл Билефельдт, числа с плавающей точкой позволяют моделировать непрерывные величины - и вы найдете их повсюду - не только в физическом мире, но даже в таких местах, как бизнес и финансы.

Я использовал математику с плавающей точкой во многих, многих областях моей карьеры программиста: химию, работу над AutoCAD и даже написание симулятора Монте-Карло для финансовых прогнозов. Фактически, есть парень по имени Дэвид Э. Шоу, который использовал прикладные методы научного моделирования с плавающей точкой на Уолл-стрит, чтобы заработать миллиарды.

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

Зачем кому-то нужен поплавок против двойника ?

В стандартном представлении IEEE 754 32-разрядное число с плавающей запятой дает вам около 7 десятичных знаков точности и показатели в диапазоне от 10 -38 до 10 38 . 64-битный дубль дает около 15 десятичных знаков точности, а показатели в диапазоне от 10 -307 до 10 307 .

Может показаться, что поплавка будет достаточно для того, что кому-то разумно понадобится, но это не так. Например, многие реальные величины измеряются более чем 7 десятичными цифрами.

Но более тонко, есть проблема, в разговорной речи называемая «ошибка округления». Двоичные представления с плавающей запятой действительны только для значений, дробные части которых имеют знаменатель со степенью 2, например 1/2, 1/4, 3/4 и т. Д. Чтобы представить другие дроби, например 1/10, вы «округлите» значение ближайшей двоичной дроби, но это немного неправильно - это «ошибка округления». Затем, когда вы делаете математику с этими неточными числами, неточности в результатах могут быть намного хуже, чем вы начали - иногда процент ошибок увеличивается или даже накапливается экспоненциально.

В любом случае, чем больше двоичных цифр вам нужно работать, тем ближе будет округленное двоичное представление к числу, которое вы пытаетесь представить, поэтому ошибка округления будет меньше. Затем, когда вы выполняете математические расчеты, если у вас есть много цифр для работы, вы можете сделать гораздо больше операций, прежде чем накопительная ошибка округления возрастет до места, где это является проблемой.

На самом деле, 64-битные двойные числа с их 15 десятичными цифрами не достаточно хороши для многих приложений. В 1985 году я использовал 80-битные числа с плавающей запятой, и теперь IEEE определяет 128-битный (16-байтовый) тип с плавающей запятой, который я могу себе представить.


2
+1 Боб Мой опыт работы с системами управления с высоким разрешением, такими как телескоп для астрономии, заключается в том, что 64-битные двойные числа не достаточно хороши, если вы не разберетесь с условиями. То же самое для управления огнем и навигации на большие расстояния
Тим Williscroft

20

Это распространенное заблуждение, что везде, где вы имеете дело с деньгами, вы должны хранить их значение в виде целого числа (центов). Хотя в некоторых простых случаях, таких как онлайн-магазин, это правда, если у вас есть что-то более продвинутое, это не очень помогает.

Давайте рассмотрим пример: разработчик зарабатывает 100 000 долларов в год. Какова его точная месячная зарплата? Используя целое число, вы получите результат $ 8333,33 (33 833333), умноженный на 12, - $ 99 999,96. Помогло ли это как целое число? Нет, это не так.

Банки всегда используют десятичные / целочисленные значения? Ну, они делают для транзакционной части. Но, например, как только вы начинаете говорить об инвестиционном банкинге, за исключением отслеживания реальных транзакций, все остальное является плавающим. Поскольку это весь внутренний код, вы его не увидите, но вы можете взять пик на QuantLib , который по сути тот же (за исключением гораздо чище ;-).

Зачем использовать поплавки? Потому что использование десятичной дроби совсем не помогает, когда вы используете такие функции, как квадратный корень, логарифмы, степени с нецелыми показателями и т. Д. И, конечно, числа с плавающей запятой намного быстрее, чем типы десятичных чисел.


1
@Job - десятичные дроби и числа с плавающей точкой очень разные. Вы можете хранить 0.1 точно в десятичном виде, но не в формате с плавающей запятой или двойном.
Скотт Уитлок

3
У меня был другой вопрос. Если вы заплатили $100,000/12и использовали поплавок. Почему результат будет ровно 100 000 долларов? Почему плавающий (или десятичный) округлять вверх или вниз каждый раз, когда кому-то платят? Я имею в виду, когда пишу чек (вы не можете сделать 1/2 или 1/3 цента) или прямой депозит (я предполагаю, что он имеет те же ограничения)

@acid: >>> x = 100000 / 12.0 >>> x * 12 100000.0
vartec

перечитать мой комментарий? У меня вопрос, когда я использую программное обеспечение для создания чека каждый месяц. Поскольку никто не может заплатить 1/2 цента, как человек получает полную сумму через год?

2
@acid: вы не можете использовать прямое деление, независимо от того, используете ли вы целое число, десятичное число или деление в виде числа с плавающей точкой, а затем округленного В этом все дело, использование десятичной дроби в этом случае не поможет.
vartec

4

То, что вы описали, является отличным обходным решением для ситуаций, когда вы контролируете все входы и выходы .

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

На самом деле вы столкнетесь с этими проблемами, даже если будете использовать перечисленные трюки. При расчете 17,5% налога с цены вы получите дробные центы, независимо от того, сохраняете ли вы стоимость в долларах или центах. Вы должны сделать правильное округление, так как налоговый работник очень расстроен, если вы не заплатите ему достаточно. Использование правильных moneyтипов (какими бы они ни были на языке, который вы используете) избавит вас от боли.


Какой тип денег? (язык или ссылка) и почему это «правильный» тип? Это потому что ... 128 бит или что-то еще? Мой другой, почему использование моих «уловок» было бы неправильным? У вас есть целое число за цент. Если вы умножите его на 0,175, вы получите целое число и будете использовать его как угодно. Думая о вашем примере, я думаю, что float сможет хранить мои значения с достаточной точностью, но мне не нужно беспокоиться о том, что 0.3f == 0.3d будет ложным. -edit- и +1

1
@ acidzombie24 - я не имел в виду конкретный тип, но какой тип текста использует ваш язык для представления денежных значений. Кроме того, если у вас есть 10 центов и умножьте на 0,175, у вас будет 1,75 цента - как вы справляетесь с целочисленной арифметикой? Это 1 цент или 2 цента? Поймите это неправильно, и ваш клиент может в конечном итоге иметь у налоговика большие деньги.
ChrisF

Вы никогда не должны умножать 10 (целое число) на 0,175 (действительное / плавающее число), потому что вы не должны смешивать точные числа с неточными числами; результат будет неточным. Другими словами, в системе точных чисел значение типа .175 никогда не будет существовать, и поэтому это бессмысленный расчет. Лучшее решение - умножить 10000 на 175 и вручную вставить десятичную точку, где это необходимо.
Барри Браун

8
@ Барри - я знаю. Я пытался проиллюстрировать тип проблемы, которую вы получаете. Также существует значение, например, 0,175, если ставка налога составляет 17,5%, и вам необходимо рассчитать налог на предмет, который стоит 10 центов.
ChrisF

1
@acidzombie: правильный тип для денег - это десятичная дробь с фиксированной запятой с высокой точностью (не менее 4 десятичных). Нет, если, и, или но. Сохранение значения денег в качестве центов не достаточно, потому что на практике это дает вам только две точки точности.
Aaronaught

3

«Бог сотворил целые числа, все остальное - дело человека». - Леопольд Кронекер (1886).

По определению вам не нужны никакие другие виды чисел. Полнота Тьюринга для языка программирования основана на простых отношениях между различными типами чисел. Если вы можете работать с целыми числами (a / k / a натуральными числами), вы можете делать что угодно.

Вопрос довольно странный, потому что они вам не нужны . Может быть, вы хотите места, где это удобно или оптимально или дешевле или что-то?


7
Мы также можем обойтись без целых чисел, поскольку их также можно построить, используя только теоретико-множественные операции и пустое множество. Но и то, и другое из полноты Тьюринга являются академическим редукционизмом, доведенным до крайности.
Боб Мерфи

4
Кроме того, полнота Тьюринга относится только к вычислениям. Ни целые числа, ни даже рациональные числа не являются математически полными, поскольку ни они не закрыты для сходимости последовательностей Коши. Итак, Кронекер был полон горячего воздуха: если вы хотите получить полное метрическое пространство, которое включает в себя целые числа, вы должны получить реальный результат: xkcd.com/849
Боб Мерфи

1
@ Боб Мерфи: «академический редукционизм доведен до крайности». Точно. Вопрос плохой и приводит к этому как возможный ответ.
С.Лотт

2

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

Откровенно говоря, говорить, что вам не нужны числа с плавающей точкой, потому что вы знаете, как выполнять десятичную математику с использованием целых чисел, - это все равно, что говорить, что вы знаете, как делать арифметические рукописные записи, так зачем использовать калькулятор? Итак, вы знаете концепцию; Браво. Это не значит, что вы должны постоянно использовать эти знания. Часто для недвоичного бизона быстрее, дешевле и понятнее просто сказать 3,5 + 4,6 = 8,1, чем преобразовывать сиг-фиг в целое число.


1

Основное преимущество типов с плавающей запятой состоит в том, что с точки зрения времени выполнения, два или три формата (я хотел бы, чтобы больше языков поддерживали 80-битные форматы) были бы достаточны для быстрого большинства вычислительных целей. Если бы языки программирования могли легко поддерживать семейство типов с фиксированной запятой, аппаратная сложность, требуемая для данного уровня производительности, часто была бы ниже с типами с фиксированной запятой, чем с плавающей запятой. К сожалению, предоставление такой поддержки далеко не «просто».

Чтобы язык программирования эффективно удовлетворял 98% числовых потребностей приложений, он должен включать десятки типов и предоставлять операции определения для сотен комбинаций; кроме того, даже если бы язык программирования имел прекрасную поддержку с фиксированной запятой, некоторым приложениям все равно нужно было бы поддерживать примерно постоянную относительную точность в достаточно большом диапазоне, чтобы требовать плавающей запятой. Учитывая, что математика с плавающей точкой в ​​некоторых случаях будет необходима в любом случае, если поставщики оборудования сосредоточатся на математической производительности с двумя или тремя форматами с плавающей точкой, а код, использующий эти форматы всякий раз, когда они работают достаточно хорошо, в целом достигнет лучших результатов. «удар на доллар», чем пытаясь оптимизировать поведение математики с фиксированной запятой.

Кстати, математика с фиксированной запятой была более выгодна с 8-битными и 16-битными процессорами, чем с 32-битными. На 8-битном процессоре в ситуации, когда 32-битного не вполне достаточно, 40-битный тип будет стоить только на 25% больше места и на 25-50% больше времени, чем 32-битный тип, и потребует 37,5% меньше места и на 37,5-60% меньше времени, чем у 64-битного типа. На 32-битной платформе, если 32-битный тип не подходит для чего-либо, часто нет особых причин использовать что-либо меньшее, чем 64-битный. Если 48-битный тип с фиксированной запятой будет адекватным, 64-битный «двойной» будет работать так же хорошо, как и тип с фиксированной запятой.


0

Как правило, вы должны быть очень осторожны в их использовании. Понимание потери точности, которая может возникнуть даже при простых вычислениях, является сложной задачей. Например, усреднение списка чисел как это очень плохая идея:

double average(List<Double> data) {
  double ans = 0;
  for(Double d : data) {
    ans += d;
  }
  return ans / data.size();
}

Причина в том, что для достаточно больших списков вы в основном теряете все точки данных, когда ansстановитесь достаточно большими (см., Например, это ). Проблема с этим кодом в том, что для небольших списков он, вероятно, просто будет работать - он ломается только в масштабе.

Лично я думаю, что вы должны использовать их только тогда, когда: а) расчет действительно должен быть быстрым; б) вас не волнует, что результат может быть далеко (если вы действительно не знаете, что делаете).


-1

Одна мысль состоит в том, что вы должны использовать плавающие или двойные представления, когда вам нужно иметь дело со значениями вне целочисленного диапазона.

Современные архитектуры (примерно) имеют диапазон целых чисел со знаком +/- 2 147 483 647 (32 бита) или +/- 9 223 372 036 854 775 807 (64 бита). Без знака увеличивает это в 2 раза.

IEEE 754 плавает (примерно) от +/- 1,4 × 10 ^ −45 до 3,4 × 10 ^ 38. Double расширяет этот диапазон до +/- 5 × 10−324 ± 2,225 × 10 ^ −308, при этом множество условий и особенностей здесь опущено.

Конечно, самая потрясающе очевидная причина в том, что вам может понадобиться представить -0 ;-)


Номера в основном из статей Википедии и предназначены для иллюстрации. За исключением -0, это просто для удовольствия.
Стивен

Проблема в том, что в этом огромном диапазоне МНОГО целых чисел, которые вообще не представлены.
Барри Браун

@BarryBrown Совершенно верно. "много условий и особенностей опущено", хотя.
Стивен

-1

Обычная причина в том, что они быстры, так как JVM обычно использует базовую аппаратную поддержку (если вы не используете строгий fp).

См. Https://stackoverflow.com/questions/517915/when-to-use-strictfp-keyword-in-java, что подразумевает строгий fp.


Математика с плавающей точкой быстрее, чем целочисленная математика? На каком процессоре вычисления с плавающей запятой занимают меньше циклов, чем целочисленные вычисления?
this.josh

1
@ this.josh, сильно зависит от количества цифр в ваших номерах. Также целые числа не могут делить точно, что может или не может быть важным.

-2

Вот почему нам нужны 256-битные операционные системы.

Длина Планки (наименьшее расстояние, которое вы можете измерить) = 10 ^ -35 м
. Наблюдаемая вселенная имеет длину 14Bn парсек = 10 ^ 25 м.
Таким образом, вы можете измерить все в единицах длины Планки в виде целых чисел, если у вас есть только 200 бит точности.


2
-1: что если вы моделируете вещи в масштабе, большем, чем наблюдаемая вселенная?
Амара

2
@ Sparkleshy, вот для чего нужны указатели FAR!
Мартин Беккет
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.