Я хочу сравнить два числа с плавающей точкой в PHP, как в следующем примере кода:
$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
В этом коде он возвращает результат else
состояния вместо if
условия, хотя $a
и $b
такой же. Есть ли какой-нибудь особый способ обработки / сравнения чисел в PHP?
Если да, то, пожалуйста, помогите мне решить эту проблему.
Или есть проблема с моим сервером конфигурации?
php
floating-point
Сантош Сонарикар
источник
источник
a and b are same
. Это ваш полный код?floating-point
описание тега? stackoverflow.com/tags/floating-point/info Такое поведение вы, скорее всего, встретите на любом языке программирования при использовании чисел с плавающей запятой. См., Например, stackoverflow.com/questions/588004/is-javascripts-math-brokenОтветы:
Если вы делаете это так, они должны быть одинаковыми. Но обратите внимание, что характеристикой значений с плавающей точкой является то, что вычисления, которые, по- видимому, приводят к одному и тому же значению, не обязательно должны быть идентичными. Так что если
$a
литерал.17
и$b
прибывает туда через вычисление, вполне может быть, что они разные, хотя оба отображают одно и то же значение.Обычно вы никогда не сравниваете значения с плавающей точкой для равенства, как это, вам нужно использовать наименьшее допустимое различие:
Что-то такое.
источник
abs($a-$b)
>abs(($a-$b)/$b)
0.10000000000000000555111512312578270211815834045410156
обычно не имеет смысла, и0.1
вместо этого они предпочли бы . И записать число так, чтобы его можно было прочитать снова точно таким же образом. Как видите, это не так ясно, как вы это представляете. И для записи, вы все еще хотите сравнить числа с плавающей запятой, как я показал, потому что вы можете прийти$a
и$b
через различные вычисления, которые могут сделать их разными.a=b=0
если иa
является наименьшим из возможных ненулевых положительных значений иb
является наименьшим ненулевым отрицательным значением, проверка будет ошибочно провалена. Немного полезной информации здесь: плавающая$b
? руководство по PHP только что сделалоif(abs($a-$b) < $epsilon)
руководство по MySQL, также сделало то же самоеHAVING ABS(a - b) <= 0.0001
$a == $b == 0
, но он уже гораздо более общий, чем абсолютная ошибка. Если$a
и$b
в миллионах, то вамEPSILON
придется сильно отличаться от того, если$a
и$b
где-то близко0
. См. Ссылку Dom выше для лучшего обсуждения этоПрочитайте красное предупреждение в руководствеСначала . Вы никогда не должны сравнивать поплавки на равенство. Вы должны использовать технику эпсилон.
Например:
где
PHP_FLOAT_EPSILON
константа, представляющая очень небольшое число (вы должны определить его в старых версиях PHP до 7.2)источник
EPSILON
здесь произвольная пользовательская константа. В PHP нет встроенной константы, представляющей специфическую идею архитектуры epsilon. (См. Такжеget_defined_constants
.)PHP_FLOAT_EPSILON
Наименьшее представимое положительное число x, так что x + 1.0! = 1.0. Доступно с PHP 7.2.0.echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */
Вопрос в том, как вы хотите определить «равный» для вашего приложения, насколько близко должны быть числа, чтобы считаться равными.Или попробуйте использовать математические функции:
Результат:
источник
bccomp()
принимает строки в качестве аргументов. В любом случае вы можете использоватьPHP_FLOAT_DIG
для аргумента масштаба.Как уже было сказано, будьте очень осторожны при выполнении сравнений с плавающей запятой (равно, больше или меньше) в PHP. Однако, если вас интересуют только несколько значащих цифр, вы можете сделать что-то вроде:
Использование округления до 2 десятичных знаков (или 3, или 4) приведет к ожидаемому результату.
источник
loose_float_compare
чтобы было понятно, что происходит.bccomp($a, $b, 2)
превосходит ваше решение. В этом примере 2 - это точность. Вы можете установить любое число с плавающей запятой, которое хотите сравнить.Было бы лучше использовать нативное сравнение PHP :
источник
Если у вас есть значения с плавающей запятой для сравнения с равенством, простой способ избежать риска внутренней стратегии округления ОС, языка, процессора и т. Д. Состоит в сравнении строкового представления значений.
Вы можете использовать любой из следующих способов для получения желаемого результата: https://3v4l.org/rUrEq
Струнное литье
Конкатенация строк
функция strval
Строковые представления гораздо менее требовательны, чем плавающие, когда дело доходит до проверки равенства.
источник
(string)
операция приведения выполняется по ссылке, изменяя исходное объявление? Если так, то это не так 3v4l.org/CraasЕсли у вас есть небольшое конечное число десятичных знаков, которое будет приемлемым, следующее прекрасно работает (хотя и с более низкой производительностью, чем решение epsilon):
источник
Это работает для меня на PHP 5.3.27.
источник
Для PHP 7.2 вы можете работать с PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):
источник
==
и ,!=
но не>
,>=
,<
,<=
Если вы напишите это так, это, вероятно, сработает, так что я полагаю, вы упростили это для вопроса. (И держать вопрос простым и кратким, как правило, очень хорошая вещь.)
Но в этом случае я предполагаю, что один результат - это вычисление, а один - постоянная величина.
Это нарушает основное правило программирования с плавающей запятой: никогда не сравнивайте равенство.
Причины этого несколько тонкие 1, но важно помнить, что они обычно не работают (за исключением, по иронии судьбы, целочисленных значений), и что альтернативой является нечеткое сравнение по направлениям:
1. Одна из основных проблем связана с тем, как мы пишем числа в программах. Мы записываем их как десятичные строки, и в результате большинство записываемых дробей не имеют точных машинных представлений. У них нет точных конечных форм, потому что они повторяются в двоичном виде. Каждая машинная дробь является рациональным числом вида x / 2 n . Теперь константы являются десятичными, и каждая десятичная константа является рациональным числом вида x / (2 n * 5 m ). Числа 5 м являются нечетными, поэтому ни для одного из них нет коэффициента 2 n . Только когда m == 0, есть конечное представление как в двоичном, так и в десятичном разложении дроби. Таким образом, 1,25 является точным, потому что это 5 / (2 2 * 5 0) но 0,1 не потому, что это 1 / (2 0 * 5 1 ). На самом деле, в серии 1.01 .. 1.99 точно представлены только 3 числа: 1.25, 1.50 и 1.75.
источник
round($float, 3) == round($other, 3)
Вот решение для сравнения чисел с плавающей запятой или десятичных чисел
Приведите
decimal
переменную кstring
и все будет в порядке.источник
Сравнение поплавков на равенство имеет наивный алгоритм O (n).
Вы должны преобразовать каждое значение с плавающей точкой в строку, а затем сравнить каждую цифру, начиная с левой стороны строкового представления каждого с плавающей запятой, используя операторы сравнения целых чисел. PHP автоматически передаст цифру в каждой позиции индекса в целое число перед сравнением. Первая цифра, большая чем другая, разорвет цикл и объявит число с плавающей точкой, к которому она принадлежит, как большее из двух. В среднем будет 1/2 * n сравнений. Для поплавков, равных друг другу, будет n сравнений. Это худший сценарий для алгоритма. В лучшем случае, первая цифра каждого числа с плавающей запятой отличается, вызывая только одно сравнение.
Вы не можете использовать INTEGER COMPARISON OPERATORS для необработанных значений с плавающей точкой с целью получения полезных результатов. Результаты таких операций не имеют значения, потому что вы не сравниваете целые числа. Вы нарушаете домен каждого оператора, который приводит к бессмысленным результатам. Это относится и к дельта-сравнению.
Используйте целочисленные операторы сравнения для того, для чего они предназначены: сравнения целых чисел.
Упрощенное решение:
источник
2019
TL; DR
Используйте мою функцию ниже, как это
if(cmpFloats($a, '==', $b)) { ... }
cmpFloats($a, '<=', $b)
противbccomp($a, $b) <= -1
Резюме
Я раскрою тайну.
Так что если вы попробуете ниже, это будет равно:
Как узнать фактическую стоимость поплавка?
Как вы можете сравнить?
==
и!=
, вы можете типизировать их со строками, это должно прекрасно работать:Тип приведения со строкой :
Или введите с
number_format()
:Предупреждение:
Избегайте решений, которые включают математическое манипулирование числами с плавающей точкой (умножение, деление и т. Д.), А затем сравнение, в основном они решают некоторые проблемы и создают другие проблемы.
Предлагаемое решение
Я создал чистую функцию PHP (без зависимостей / библиотек / расширений не требуется). Проверяет и сравнивает каждую цифру как строку. Также работает с отрицательными числами.
источник
У функции из @evilReiko есть некоторые ошибки, подобные этим:
В моей функции я исправил эти ошибки, но в любом случае в некоторых случаях эта функция возвращает неправильные ответы:
Исправлена функция сравнения поплавков
Ответ на ваш вопрос
источник
Вот полезный класс из моей личной библиотеки для работы с числами с плавающей запятой. Вы можете настроить его по своему вкусу и вставить любое понравившееся вам решение в методы класса :-).
источник
Простой ответ:
источник