выдача в двойном значении сравнения с php - PullRequest
6 голосов
/ 26 марта 2012

Я пытаюсь создать контрольные примеры для проверки правильности или неправильности значений в моей таблице.

Это мои коды

echo $a = 2/9;
echo "<br>".$b=0.22222222222222;

echo "<br>".gettype($a);
echo "<br>".gettype($b);
if($a==$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";


if((string)$a==(string)$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";

Почему сначала, если условие не работает?Я не могу найти причину.Пожалуйста, помогите мне.

Ответы [ 3 ]

10 голосов
/ 26 марта 2012

Тест нарушает основное правило программирования с плавающей запятой: никогда не проводить сравнения на равенство.

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

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

Это в значительной степени исключает точное сравнение чисел с плавающей запятой, за исключением, по иронии судьбы, между целыми значениями. Вам необходимо реализовать нечеткое сравнение, такое как abs(a - b) < epsilon.

И на самом деле ваш 2/9 - это случай с джекпотом, который не имеет конечного представления как десятичная строка или двоичная строка! 1

Для успешного сравнения 2/9 на равенство сконстанта предъявляет к программе, интерпретатору и библиотеке больше требований к совершенству, чем можно рассчитывать.

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

И на самом деле это хуже , чем этопотому что отдельные компоненты 0,2 / 10 n в десятичной строке также не имеют точных двоичных эквивалентов.Таким образом, вполне вероятно, что действительно совершенное и точное преобразование 0.22222222222222 действительно не фактически соответствует представлению с максимальным усилием действительного 2/9.Вы не можете выразить в виде конечной десятичной строки точную дробь base-2, которая наиболее точно представляет 2/9 в любом конкретном (конечном) количестве бит.

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


1.Каждая дробь машины - это рациональное число вида х / 2 n .Теперь константы являются десятичными, и каждая десятичная константа является рациональным числом вида x / (2 n * 5 m ).Числа 5 m являются нечетными, поэтому для любого из них нет коэффициента 2 n .Только когда m == 0, есть конечное представление как в двоичном, так и в десятичном разложении дроби.Например, 1.25 является точным, потому что это 5 / (2 2 * 5 0 ), но 0.1 не потому, что это 1 / (2 0 * 5 * 1 * тысяча семьдесят-три ).А для рационального числа 2/9 не существует ни 2 n , ни a 5 m factor.

5 голосов
/ 26 марта 2012

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

$a = 2/9;
$b=0.22222222222222;

$a = number_format($a, 9);
$b = number_format($b, 9);

echo "a = " . $a . " and b = " . $b;

echo "<br>".gettype($a);
echo "<br>".gettype($b);
if($a==$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";


if((string)$a==(string)$b){
    echo "<br>". "equal";
}else echo "<br>". "Not equal";
3 голосов
/ 26 марта 2012

Если вы посмотрите на PHP документацию чисел с плавающей запятой (которая включает в себя двойные числа), вы быстро увидите, что это невероятно трудно сравнивать из-за природы чисел с плавающей запятой.

Поэтому никогда не доверяйте результатам с плавающей запятой последней цифре и не сравнивайте числа с плавающей запятой непосредственно на равенство.

В документации также приведен пример:

<?php

$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;

if(abs($a-$b) < $epsilon) {
    echo "true";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...