Почему языки программирования округляются до 0,6? - PullRequest
32 голосов
/ 06 января 2010

Если вы поставите десятичное число в формате, где должно быть округлено до ближайшего десятого, и оно будет равно 1,55, оно округляется до 1,5. 1,56 будет округляться до 1,6. В школе я вспоминаю, как узнал, что вы округлились, когда достигли пяти лет, и понизили, если это 4 или ниже. Почему в Питоне все по-другому, и др.

Вот пример кода для Python 2.6x (какой бы ни была последняя версия)

'{0:01.2f}'.format(5.555)  # This will return '5.55'

Попробовав некоторые из приведенных примеров, я понял что-то еще более запутанное:

'{0:01.1f}'.format(5.55)  # This will return '5.5'
# But then
'{0:01.1f}'.format(1.55)  # This will return '1.6'

Почему разница при использовании 1,55 против 5,55. И то и другое печатается как литералы (так плавает)

Ответы [ 7 ]

85 голосов
/ 06 января 2010

Во-первых, в большинстве языков неокрашенная константа, такая как "1.55", обрабатывается как значение двойной точности. Тем не менее, 1,55 не совсем точно представляется как значение двойной точности, потому что у него нет завершающего представления в двоичном виде. Это вызывает много любопытных поведений, но один эффект заключается в том, что когда вы набираете 1.55, вы на самом деле не получаете значение, которое находится точно на полпути между 1.5 и 1.6.

В двоичном формате десятичное число 1,55:

1.10001100110011001100110011001100110011001100110011001100110011001100...

Когда вы набираете «1,55», это значение фактически округляется до ближайшего представимого значения двойной точности (во многих системах ... но есть исключения, к которым я доберусь). Это значение:

1.1000110011001100110011001100110011001100110011001101

что немного больше чем 1,55; в десятичном виде это точно:

1.5500000000000000444089209850062616169452667236328125

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

Но подождите, в вашей системе, «1,55» округлено вниз , а не вверх. Что происходит?

Это может быть несколько разных вещей, но наиболее вероятно, что вы находитесь на платформе (возможно, Windows), которая по умолчанию выполняет арифметику с плавающей запятой, используя инструкции x87, которые используют другой (80-битный) внутренний формат. В 80-битном формате 1,55 имеет значение:

1.100011001100110011001100110011001100110011001100110011001100110

что немного меньше чем 1,55; в десятичном виде это число:

1.54999999999999999995663191310057982263970188796520233154296875

Поскольку оно меньше 1,55, оно округляется на вниз , когда округляется до одной цифры после десятичной точки, давая результат "1,5", который вы наблюдаете.

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

>>> "%.0f" % 0.5
'0'
>>> "%.0f" % 1.5
'2'

Обратите внимание, что оба значения округляются до четных чисел; ни один не округляет до «1».

Редактировать: в своем пересмотренном вопросе вы, похоже, переключились на другой интерпретатор python, для которого с плавающей запятой используется двойной тип IEEE754, а не 80-битный тип x87. Таким образом, «1,55» округляет вверх , как в моем первом примере, но «5,55» преобразуется в следующее двоичное значение с плавающей запятой:

101.10001100110011001100110011001100110011001100110011

что точно:

5.54999999999999982236431605997495353221893310546875

в десятичном виде; так как это меньше чем 5,55, оно округляется вниз .

5 голосов
/ 06 января 2010

Есть много способов округлить числа. Подробнее о округлении вы можете прочитать в Википедии . Метод округления, используемый в Python: Округление наполовину от нуля , а метод округления, который вы описываете, более или менее такой же (по крайней мере, для положительных чисел).

2 голосов
/ 06 января 2010

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

1.5 rounds to 2
2.5 rounds to 2
3.5 rounds to 4
4.5 rounds to 4

т.е. значение .5 будет округлено до ближайшего четного целого числа. Причина этого заключается в том, что в конечном итоге округление большого числа чисел выровняется. Если, например, банк должен оплатить проценты миллиону клиентов, и 10% из них в итоге получат округление до 5 центов, банк выплатит на 500 долларов больше, если вместо этого будут округлены значения.

Другой причиной неожиданного округления является точность чисел с плавающей запятой. Большинство чисел не могут быть представлены точно, поэтому они представлены в ближайшем приближении. Когда вы думаете, что у вас есть число, равное 1,55, вы можете фактически получить число, подобное 1,54999. Конечно, округление этого числа до одного десятичного знака приведет к 1,5, а не к 1,6.

2 голосов
/ 06 января 2010

Округление и усечение различны для каждого языка программирования, поэтому ваш вопрос, вероятно, напрямую связан с Python.

Однако округление как практика зависит от вашей методологии.

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

Редактировать: По некоторым другим постерам кажется, что Python не демонстрирует поведение округления, которое вы описали:

>>> "%0.2f" % 1.55 
'1.55' 
>>> "%0.1f" % 1.55 
'1.6' 
>>> "%0.0f" % 1.55 
'2' 
2 голосов
/ 06 января 2010

Похоже, что это не так. Вы используете формататор строк с плавающей точкой, верно?

>>> "%0.2f" % 1.55
'1.55'
>>> "%0.1f" % 1.55
'1.6'
>>> "%0.0f" % 1.55
'2'
2 голосов
/ 06 января 2010

Можете ли вы привести пример кода, потому что это не то поведение, которое я вижу в Python:

>>> "%.1f" % 1.54
'1.5'
>>> "%.1f" % 1.55
'1.6'
>>> "%.1f" % 1.56
'1.6'
0 голосов
/ 24 февраля 2011

Один из способов покончить с хотя бы одним аспектом проблем округления (хотя бы иногда) - это выполнить некоторую предварительную обработку. Форматы одинарной и двойной точности могут представлять все целые числа точно от -2 ^ 24-1 до 2 ^ 24-1 и от -2 ^ 53-1 до 2 ^ 53-1 соответственно. Что можно сделать с действительным числом (с ненулевой дробной частью) до

  1. снимите знак и оставьте его на потом
  2. умножьте оставшееся положительное число на 10 ^ (необходимое количество десятичных знаков)
  3. добавить 0,5, если режим округления в вашей среде установлен на рубить (округлить до нуля)
  4. округлить число до ближайшего
  5. sprintf число в строку с 0 десятичными числами в формате
  6. «вручную» форматировать строку в соответствии с ее длиной после sprintf, количеством требуемых десятичных знаков, десятичной запятой и знаком
  7. строка должна теперь содержать точное число

Имейте в виду, что если результат после шага 3 превысит диапазон определенного формата (см. Выше), ваш ответ будет неправильным.

...