2

Полный текст вопроса: В какой из перечисленных строк кода присутствует потенциальная логическая ошибка сравнения дробных чисел?

введите сюда описание изображения

Правильных ответов может быть несколько.

1) Я не очень понимаю вопрос. Что имеют ввиду под "потенциальная логическая ошибка"?

2) Все сравнения которые есть я набил проверил http://ideone.com/xNE6TB не true только вторая строка:

2-0.8 === 1+1.2

Но я так понял что она не true не потому что там логическая ошибка, а тупо цифры разные выходит сравниваем 1.2 и 2.2 Но ответ только вторая строчка не верен. Помогите нубу кто чем может с разъяснениями.

2 Answers2

3

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

К примеру, то же сравнение 6/5 === 1.2, вполне может быть представлено в памяти как 6/5 === 1.2000000000000004, а итог сравнения как 1.19999999999998 == 1.2000000000000004. Поэтому с такими числами нужна аккуратность в обращении. Как правило числа сравнивают с какой-либо малой разностью abs(6/5 - 1.2) < 0.00001. Сравнивать их прямым == или === считается логической ошибкой, поскольку является бомбой замедленного действия.

Поэтому получаем:

6/5 === 1.2 - неверно 
2-0.8 === 1+1.2 - спорно, но можно считать верным (всегда false)
abs(6/5 - 1.2) < 0.0001 - верно
1.2 === 1.2 - неверно
0.0001 > abs(6/5 - 1.2) - верно
Alex Krass
  • 17,744
  • "неверно" - это те строки где есть потенциальная логическая ошибка да? А 1.2 === 1.2 неверно именно из-за того что там числа с плавающей точкой тоже? – user221343 Dec 04 '16 at 21:18
  • @RoboNoob, да, строки с потенциальной логической ошибкой помечены как "неверно". В том числе проверка 1.2 === 1.2 хоть и будет работать в 99% случаев, но т.к. там числа с плавающей точкой, остается шанс получить логическую ошибку. Дело в том, что данный код может работать нормально, а может в какой-то момент перестать работать (поменяли сервер, поменяли версию PHP или просто звезды не так сошлись). Никто не гарантирует правильность такого сравнения чисел, так как после 14-го знака там может появиться все что угодно. – Alex Krass Dec 05 '16 at 06:53
  • var_dump(6 / 5 === 1.2); --> bool(true), var_dump(1.2 === 1.2); --> bool(true). – Visman Dec 05 '16 at 08:04
  • @Visman, читайте внимательно информацию по предоставленным ссылкам и учите мат. часть, ваш true не гарантирован. То, что вы получили bool(true) является всего лишь частным случаем вероятности и зависит от операционной системы и реализации интерпретатора. При определенных условиях Вы можете спокойно словить bool(false) и интерпретатор будет прав. Даже в документации про это специально написали и выделили красным цветом. – Alex Krass Dec 05 '16 at 08:58
  • @AlexKrass, для сравнения типа 1.2 === 1.2 всегда будет true. – Visman Dec 05 '16 at 09:07
  • @Visman Я вот смотрю официальную документацию и вижу, что мне никто этого не гарантирует. Смотрю IEEE 754 и опять же вижу, что мне никто это не гарантирует. Откуда Вы берете уверенность, что 1.2 === 1.2 всегда и везде будет true? Вы можете это хоть как-то доказать? Нельзя полагаться на недокументированное поведение, это и считается потенциальной логической ошибкой, которая может себя не проявлять вообще никак долгое время. – Alex Krass Dec 05 '16 at 09:12
  • @AlexKrass, то есть в документации у вас прямо сказано, что одно и то же число может хранится по разному, в результате чего при сравнении переменных, которые содержат это число, мы получим false? Это же бред! – Visman Dec 05 '16 at 09:17
  • @Visman, ну это не у меня в документации сказано, а вообще относится к числам с плавающей точкой(о них много написано). Их можно считать равными только до определенной точности, несколько знаков после запятой, дальше может идти все, что угодно. В теории любая попытка прямого сравнения таких чисел является ошибкой. На практике, в выражении 1.2 === 1.2 всегда будет true, но общей потенциальной ошибкой это быть не перестает т.к. это не гарантировано, если что-то внутри интерпретатора PHP в будущем поменяется. – Alex Krass Dec 05 '16 at 09:41
  • @AlexKrass, да не может такое быть не при каких условиях! Ладно если бы одно число хранили с двойной точностью, а другое с одинарной, одно в виде строки, другое в виде числа. Но строгое равенство подразумевает один тип в обеих частях сравнения. – Visman Dec 05 '16 at 09:46
  • @Visman, ну возьмите нетривиальный пример и скажите, почему данное выражение var_dump(0.1 + 0.7, 0.8, 0.1 + 0.7 === 0.8) дает float(0.8) float(0.8) bool(false), не смотря на соблюдение типов в обоих частях. – Alex Krass Dec 05 '16 at 10:01
  • @AlexKrass, этот пример относится к 6 / 5 === 1.2 или к 2 - 0.8 === 1 + 1.2 варианту, так как там идет вычисление перед сравнением. В варианте 1.2 === 1.2 нет ни каких вычислений. – Visman Dec 05 '16 at 10:09
  • 1
    @Visman, допустим, но я же получаю одинаковые числа float(0.8) и float(0.8), но они не равны. Тоже самое с 1.2 === 1.2, они равны только потому, что в исходниках PHP одинаковый код генерации. Но вот гарантий того, что в один прекрасный момент это не изменится в исходниках, нету. И если интерпретатор вдруг начнет выдавать разные последовательности для двух якобы одинаковых чисел по каким-либо причинам, предъявить ему Вы ничего не сможете. Это потенциальная ошибка, шанс столкнуться с который стремится к нулю, но все еще ошибка. Довольно узкий момент теории, на который практикам все равно. – Alex Krass Dec 05 '16 at 10:15
  • Да нет такого в теории. – Qwertiy Dec 05 '16 at 14:36
1

6 / 5 === 1.2

5 не является степенью 2, поэтому дробь точно не представима.
Сравнивать на равенство не стоит.

2 - 0.8 === 1 + 1.2

То же, что в прошлый раз.
Плюс, что-то эти числа вообще не равны.

abs(6 / 5 - 1.2) < 0.0001

Это правильная форма проверки первого равенства.
Хотя я бы поставил 1e-7 вместо 1e-4.

1.2 === 1.2

По идее, две литеральные константы между собой равны. Но это странно.
Связанный вопрос: Безопасно ли сравнение == для типа double?

0.0001 > abs (6 / 5 - 1.2)

Верная форма сравнения на !== для первого пункта.

Qwertiy
  • 123,725
  • var_dump(6 / 5 === 1.2); --> bool(true), var_dump(1.2 === 1.2); --> bool(true). – Visman Dec 05 '16 at 08:04
  • @Visman, вопрос: "потенциальная логическая ошибка", т. е. то, что два неточно представленных числа в данном случае совпадают не важно. Важно, что они точно не представимы, поэтому потенциальная ошибка тут есть. – Qwertiy Dec 05 '16 at 08:17
  • Во-втором случае (1.2 === 1.2) нет, в первом согласен - есть. – Visman Dec 05 '16 at 08:18