Нет сомнений: PHP — простой, гибкий и прощающий язык. Но он также может демонстрировать удивительное поведение. В этой статье я представлю некоторые «странные факты» и объясню, почему PHP дает результаты, которые он дает.
Неточность с плавающей точкой
Большинство из вас, вероятно, знают, что числа с плавающей точкой не могут действительно представлять все действительные числа. Кроме того, некоторые операции между двумя хорошо представленными числами могут привести к неожиданным ситуациям. Это потому, что конечная точность, с которой компьютеры представляют числа, влияет на не только PHP, но и все языки программирования. Неточности в задачах с плавающей запятой вызывали головную боль у программистов с самого начала дисциплины.
Об этом вопросе было сказано много слов, но одна из самых важных написанных статей — « Что должен знать каждый учёный-компьютерщик об арифметике с плавающей точкой» . Если у вас никогда не было возможности прочитать его, я настоятельно рекомендую вам это сделать.
Давайте посмотрим на этот очень маленький фрагмент:
<?php
echo (int) ((0.1 + 0.7) * 10);
Как вы думаете, что будет результатом? Если вы догадались 8, вы ошиблись бы … Извините, вы потеряли приз! Этот код на самом деле напечатает 7! Для тех, у кого есть Zend Certification, этот пример уже известен. Фактически, вы можете найти его в Zend PHP 5 Certification Study Guide .
Теперь посмотрим, почему это происходит. Первая выполненная операция:
0.1 + 0.7 // result is 0.79999999999
Результат арифметического выражения хранится внутри как 0,7999999, а не 0,8 из-за проблемы точности, и именно здесь начинается проблема.
Вторая выполненная операция:
0.79999999 * 10 = 7.999999999
Эта операция работает хорошо, но сохраняет ошибку.
Третья и последняя выполненная операция:
(int) 7.9999999 // result is 7
Выражение использует явное приведение. Когда значение конвертируется в int
Здесь следует отметить две интересные вещи:
- Если вы разыгрываете число с
float
int
- Хотя процессор не знает, и мы на самом деле получили эту ошибку из-за ее неточностей, он работает в соответствии с математическим парадоксом, который утверждает, что 0,999 … равно 1 .
В заключение я хотел бы привести второе руководство по сертификации Zend PHP 5 :
Всякий раз, когда точность ваших вычислений является важным фактором для правильного функционирования вашего приложения, вы должны вместо этого использовать (sic) функции произвольной точности, предоставляемые расширением BCMath (вы можете найти его в своей копии руководства по PHP). встроенных типов данных PHP.
Как PHP «увеличивает» строки
Во время нашей повседневной работы мы пишем инструкции, которые выполняют операции увеличения / уменьшения, например:
<?php
$a = 5;
$b = 6;
$a++;
++$b;
Каждый легко понимает, что происходит с этими утверждениями. Но, учитывая следующие утверждения, угадайте, какой будет результат:
<?php
$a = 1;
$b = 3;
echo $a++ + $b;
echo $a + ++$b;
echo ++$a + $b++;
Вы записали свои ответы? Хорошо. Давайте проверим их всех.
4 6 7
Не слишком сложно, правда? Теперь давайте немного поднимем планку. Вы когда-нибудь пытались увеличить строки? Попробуйте угадать, какой будет выход для следующего:
<?php
$a = 'fact_2';
echo ++$a;
$a = '2nd_fact';
echo ++$a;
$a = 'a_fact';
echo ++$a;
$a = 'a_fact?';
echo ++$a;
Это не так просто на этот раз, не так ли? Давайте раскроем ответы.
fact_3 2nd_facu a_facu факт?
Удивлены? Использование оператора приращения в строке, заканчивающейся цифрой, увеличивает букву (ту, которая следует за ней в алфавите, например, t -> u). Независимо от того, начинается ли строка цифрами или нет, последний символ изменяется. Но нет никакого эффекта, если строка заканчивается не буквенно-цифровым символом.
Это хорошо задокументировано официальной документацией об увеличении / уменьшении операторов, но большинство из вас, возможно, не читали его, потому что вы, вероятно, думали, что ничего особенного там не было. Должен признать, я думал то же самое до недавнего времени.
PHP следует соглашению Perl при работе с арифметическими операциями над символьными переменными, а не с Си. Например, в PHP и Perl $ a = ‘Z’; $ А ++; превращает $ a в ‘AA’, а в C a = ‘Z’; A ++; превращает a в ‘[‘ (значение ASCII в ‘Z’ равно 90, значение ASCII в ‘[‘ равно 91). Обратите внимание, что символьные переменные можно увеличивать, но не уменьшать, и даже в этом случае поддерживаются только простые символы ASCII (az и AZ). Увеличение / уменьшение других символьных переменных не имеет никакого эффекта, исходная строка не изменяется.
Тайна появления ценности
Вы настоящий мастер массивов в PHP, признайте это. Не стесняйся. Вы уже знаете все о том, как создавать, управлять и удалять массивы. Тем не менее, следующий пример может удивить вас с первого взгляда.
Часто вы работаете с массивами и должны искать, находится ли заданное значение в массиве. PHP имеет функцию для этого, in_array()
Давайте посмотрим на это в действии:
<?php
$array = array(
'isReady' => false,
'isPHP' => true,
'isStrange' => true
);
var_dump(in_array('sitepoint.com', $array));
Какой будет выход? Делайте ваши ставки, и давайте проверим это.
правда
Разве это не немного странно? У нас есть ассоциативный массив, где все его значения логические, и если мы ищем строку, мы получаем true
Это значение волшебным образом появилось? Давайте посмотрим на другой пример.
<?php
$array = array(
'count' => 1,
'references' => 0,
'ghosts' => 1
);
var_dump(in_array('aurelio', $array));
И что мы получаем …
правда
Еще раз функция in_array()
Как это возможно?
Вы испытываете одну из лучших и худших, любимых и ненавистных функций PHP. PHP не является строго типизированным, и именно здесь начинается ошибка. Фактически, все следующие значения, если сравнивать в PHP, считаются одинаковыми, если вы не используете оператор идентификации:
- 0
- ложный
- «»
- «0»
- ЗНАЧЕНИЕ NULL
- Массив ()
По умолчанию функция in_array()
""
"0"
true
1
Следовательно, в первом примере мы получили истину, потому что 'sitepoint.com' == true
'aurelio' == 0
Чтобы решить эту проблему, вы должны использовать третий и необязательный параметр in_array()
Итак, если мы напишем:
<?php
$array = array(
'count' => 1,
'references' => 0,
'ghosts' => 1
);
var_dump(in_array('aurelio', $array, true));
мы, наконец, false
Выводы
В этой статье вы видели странное и неожиданное поведение, которое может возникнуть при использовании PHP. Уроки, которые можно извлечь из этих примеров:
- Никогда не доверяйте числам с плавающей запятой
- Дважды проверьте, какой тип данных вы используете, прежде чем использовать их в операциях
- Помните о проблемах слабых сравнений и типов данных
Если вы опытный пользователь, вы, вероятно, уже знали его, но переподготовка никогда не бывает бесполезной.
Изображение через Fotolia