PHP float расчет 2 десятичной точки - PullRequest
17 голосов
/ 22 октября 2009

Получена еще одна математическая задача.

$a = 34.56

$b = 34.55

$a сделать некоторые расчеты, чтобы получить эту цифру

$b выполняет округление до 0,05, чтобы получить эту цифру

что происходит

$c = $b - $a

предположительно, я должен быть -0,01, но я повторяю, что $c это шоу -0.00988888888888

Я пытаюсь использовать n umber_format($c, 2), но вывод 0.00,

как я могу убедиться, что $a и $b - это ровно 2 десятичных знака, без скрытого числа на обороте.

в моем знании php number_format может форматировать только дисплей, но на самом деле это значение не 2 десятичных,

Я надеюсь, что смогу получить помощь отсюда. Это действительно расстроило меня.

Ответы [ 7 ]

43 голосов
/ 22 октября 2009

Попробуйте sprintf("%.2f", $c);

Числа с плавающей запятой представлены в нотации IEEE на основе степеней 2, поэтому конечные десятичные числа могут не быть двоичными числами с окончанием, поэтому вы получаете завершающие цифры.

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

$a = 3456;

$b = 3455;

$c = $b - $a;

sprintf ("%.2f", $c/100.0);

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

13 голосов
/ 22 октября 2009

Использование round():

$c = round($b - $a, 2);

Примечание: Вы также можете выбрать соответствующий режим округления.

Редактировать: Хорошо, я не понимаю, что делает sprintf(), а не number_format():

$c = number_format($b - $a, 2);

против

$c = sprintf("%.2f", $b - $a);

4 голосов
/ 21 марта 2014

Я также столкнулся с этой проблемой недавно, когда делал вычисления с плавающей точкой. Например, у меня было 2 числа с плавающей точкой, которые при вычитании и форматировании имели значение -0,00.

$floatOne = 267.58;
$floatTwo = 267.58;
$result = number_format($floatOne - floatTwo, 2);
print $result; //printed a string -0.00

То, что я сделал, было:

$result = abs($floatOne - $floatTwo);// Made the number positive
print money_format('%i', $result); // printed the desired precision 0.00

В моем решении я знаю, что floatOne никогда не будет меньше, чем floatTwo. Функция money_format определяется только в том случае, если система имеет возможности strfmon, а Windows - нет.

4 голосов
/ 04 апреля 2013

Собственный расчет:

$a = 34.56;
$b = 34.55;
$c = $b - $a; // -0.010000000000005    

Работает как ожидалось (! Всегда используйте функции BC для вычислений действительных чисел, проблема для всех платформ на основе C):

$a = '34.56';
$b = '34.55';
$c = bcsub($b, $a, 4); // -0.0100    
3 голосов
/ 17 февраля 2010

Вы можете очень аккуратно обойти все эти проблемы, просто используя библиотеку bcmath.

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

3 голосов
/ 22 октября 2009

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

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

1 голос
/ 18 декабря 2014

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

Чтобы проиллюстрировать проблему, я объясню почему на простом примере ниже.

Он не имеет прямого отношения к PHP и не является ошибкой. Однако каждый программист должен знать об этой проблеме.

Эта проблема унесла много жизней два десятилетия назад.

25 февраля 1991 года эта проблема при вычислении плавающего числа в ракетной батарее MIM-104 Patriot не позволила перехватить поступающую ракету "Скад" в Дахране, Саудовская Аравия, что привело к гибели 28 солдат из 14-го отряда интригандеров армии США.

Но почему это происходит?

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

Просто простой пример:

$a = '36';
$b = '-35.99';
echo ($a + $b);

Вы ожидаете, что он напечатает 0,01, верно? Но он напечатает очень странный ответ, такой как 0.009999999999998

Как и другие числа, числа с плавающей запятой double или float хранятся в памяти в виде строки из 0 и 1. Отличие числа с плавающей точкой от целого состоит в том, как мы интерпретируем 0 и 1, когда хотим посмотреть на них. Есть много стандартов, как они хранятся.

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

Десятичные числа плохо представлены в двоичном виде из-за недостатка места. Итак, вы не можете выразить 1/3 точно так же, как это 0,3333333 ..., верно? Почему мы не можем представить 0.01 как двоичное число с плавающей точкой по той же причине. 1/100 - это 0,00000010100011110101110000 ..... с повторением 10100011110101110000.

Если 0,01 хранится в упрощенном и усеченном системой виде 01000111101011100001010 в двоичном формате, то при переводе обратно в десятичное число оно будет читаться как 0,0099999 .... в зависимости от системы (64-битные компьютеры обеспечат вам гораздо большую точность, чем 32 бита). В этом случае операционная система решает, печатать ли ее, как она видит, или сделать ее более понятной для человека. Таким образом, это зависит от машины, как они хотят представить это. Но это может быть защищено на уровне языка различными способами.

Если вы отформатируете результат, введите echo number_format (0.009999999999998, 2); он напечатает 0,01.

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

...