Как стать автором
Обновить

Комментарии 109

А у нас в офисе висит настоящий бубен (кожанный такой с погремушками железными). Правда он почему-то не помогает =)
значит не настоящий )
Тоже хочу. Нужно будет выбить у начальства покупку бубна :)
У меня в офисе тибетский настоящий — сотруднички подарили :) помогает кстати :)
НЛО прилетело и опубликовало эту надпись здесь
Ага. Или Finntroll с Butterfly Temple вперемешку.
PHP 5.2.2
Бубен не работает.
На 5.2.4 работает :)

float(97.5) float(98)
float(97.5) float(97)
На х86-совместимых работает.
$value[0] = 97.5;

$value[1] = (9.975-9)*100;

if(($value[0])==$value[1]){echo «TRUE»;}else{echo «FALSE»;}

Вернуло FALSE, эм… чё-то я затупил…
А float-ы сравнивать на равенство вообще-то нельзя…
Вообще-то, можно. Но в иногда это не имеет смысла.
Не стоит вообще полагаться на точность после умножения float'ов. Ежели уж где-то совсем никак юзать sprinf для удаления шлака в неинтересующих порядках. Например если var_dump'ать round(sprintf('%f',$value)) то наверное всё одинаково выведется.
Угу, это в мануале написано большими красными буквами: ru.php.net/float
actionscript 2.0

97.5, 98
97.5, 97

actionscript 3.0
97.5, 98
97.49999999999997,97

хм…
Все правильно. Нужно учесть внутренее представление вещественных чисел в компьютере. Мантисса всегда содержит какую-то маленькую ошибку, в процессе проведения операций эти ошибки могут накапливаться.
(9.975-9)*100 это на самом деле 97.499999999999972, и оно должно округлиться именно до 97 по правилам округления чисел.

Прикол из той же серии:
0.1 + 0.1 + 0.1 — 0.3 = 5.5511151231257827e-17 != 0
НЛО прилетело и опубликовало эту надпись здесь
Да. Причем, эта на 1 в конце на самом деле — результат округления чего-то еще более лохматого.
смею предположить, что в PHP реализовано статистическое округление, и в отличии от математического, статистическое поочередно округляет 0,5 в разные стороны, то до 0, то до 1.
Как это в математических операциях не математическое округление? Ошибка в мантиссе — это самое правдоподобное объяснение. Сами с этим сталкивались не раз, приходилось вводить некоторые не очень естественные ограничения.
Т.е. round(0.5) иногда равен 0, а иногда 1? Простите, но как его тогда использовать? Если мне нужно случайное поведение — я использую random, а отнюдь не round.
Помимо математического существуют ещё и другие виды округления:
  • • банковское — округление половины (N+1 знак = 5) к ближайшему чётному. В этом случае исчезает систематическая ошибка округления при суммировании большого количества чисел.
  • • статистическое округление
  • • округление к большему, округление к +∞ (англ. ceil) — если у числа округляемые знаки не равны нулю, число округляется в большую сторону. Эту форму округления используют при расчётах с потребителями сотовые операторы связи, провайдеры интернет.
  • • округление к меньшему, округление к −∞ (англ. floor) — если у числа округляемые знаки не равны нулю, то число округляется в меньшую сторону (в случае положительных чисел округляемые знаки отбрасываются, в случае отрицательных чисел значение числа увеличивается по модулю).
  • • округление к большему по модулю (округление к бесконечности, округление от нуля) относительно редко используемая форма округления, представляет из себя симметричную версию «округления к большему».
  • • округление к меньшему по модулю, округление к нулю (англ. fix) — это самое «простое» компьютерное округление, заключающееся в «отбрасывании» «лишних» цифр. (11.9 становится равным 11, −0.9 становится равным 0, −1.1 становится равным −1). Иногда округление к нулю ещё называют англ. truncate, потому что для выполнения подобного округления достаточно при выводе числа обрезать поле вывода по нужному количеству знаков.
  • • случайное округление — если N+1 знак равен 5, то число округляется в меньшую или большую сторону в случайном порядке.
  • • чередующееся округление — каждый раз, когда N+1 знак равен 5, то число поочерёдно округляется в большую и меньшую сторону.
  • • ненулевое округление — округление производится согласно правилам математического округления, однако если округляется ненулевое число, результатом округления которого должен стать 0, округление производится «от нуля».
про мантису спорить не буду. верю. знаю что это так.
просто функция round в разных языках работает по разному, и статистическое поведение встречается достаточно часто. взять хотябы тотже .net
это не статистическое округление, а банковское, округление идёт не поочерёдно, а в сторону ближайшего чётного. Тоесть округленные 3.5 и 4.5 дадут 4. Так же ведёт себя делфи и так же, ЕМНИП, описано округление в большой советской энциклопедии.
Ну что же вы хотели от чисел с плавающей точкой.

бубен из математики:
1 = 9/9 = 9 * ( 1 / 9 ) = 9 * 0.111(1) = 0.999(9)
А вроде ещё в школе должны говорить что девятки в периоде не существует :)
Почему не существует?
Просто 0,(9) тождественно равно единице (можете убедиться, просуммировав геометрическую прогрессию)
Врёте вы все:)) Мне такого не в физмат школе, не на мат факе не говорили.
На ВМК этого почему-то не говорили :'(
На мехмате говорят :) Можно, конечно говорить что это то-же самое что единица, но упоминание (9) среди математиков считается дурным тоном :)
Еще как говорили. Фомичев лично говорил :)
— Хорошо, — сказал я. — Я тоже задам последовательность вопросов о
местоположении.
— Задавай, задавай, — пробормотал Чапаев.
— Начнем по порядку. Вот вы расчесываете лошадь. А где находится эта
лошадь?
Чапаев посмотрел на меня с изумлением.
— Ты что, Петька, совсем охренел?
— Прошу прощения?
— Вот она.
Виктор Пелевин. «Чапаев и пустота».

— PS: Так будет лучше…
Из курса математики числа не равны в том случае, если между ними есть другое число, в данном случае числа равны.
как так?
ну вот возьми два множества: (-∞,1) и [1, +∞). эти множества не пересекаются, как могут быть равны 0.(9) и 1, если они принадлежат разным непересекающимся множествам?
Тут вы не правы.
Они равны, поэтому будут оба во втором множестве.
Множества? Окей.

Разделим второе множество на два: [1, 5] и всё остальное (1). Множество [1, 5] — замкнутое (любая точка из множества A=R\[1,5] принадлежит A вместе с некоторой окрестностью). Множество является замкнутым тогда и только тогда, когда оно содержит все свои предельные точки (отсылаю к доказательству соответствующего критерия). В любой окрестности предельной точки множества содержится бесконечное множество точек этого множества (по определению предельной точки).

Выберем некоторую окрестность E точки 0.(9). Пусть радиус этой окрестности r=0.0… k...01 (k нулей после запятой). 0.(9) + r = 1.0… k...00(9). Таким образом, в выбранной окрестности E лежит бесконечное множество [1, 1.0… k...00(9)) точек из [1, 5]. Из произвольности выбора E следует то же заключение для любой окрестности. Следовательно, 0.(9) — предельная точка множества [1, 5]. Так как [1, 5] замкнуто, то оно содержит все свои предельные точки, то есть и 0.(9). Из разбиения (1) следует, что 0.(9) принадлежит множеству [1, +inf).

Угу?
ыхыхы, сталкивался 2 года назад с подобным
<? php
$L = 131.17;
$a = ($L*100);
echo $a.«
»;
$a=intval($a);
echo $a.«
»;
?>
первое echo выдаст нам 13117
а второе 13116
также интересно что при $L=131.16 все будет корректно и при $L=131.18.
чорт, br-теги распознались:)
помоему в ф-ии round() надо выставлять до какого именно числа после запятой округлять. round($value,1)
Это необязательный параметр.
$value = 97.5;
var_dump($value, round($value,1)); // float(97.5) float(97.5)

$value = (9.975-9)*100;
var_dump($value, round($value,1)); // float(97.5) float(97.5)
$value = 97.5;
var_dump($value, round(round($value,1))); // float(97.5) float(98)

$value = (9.975-9)*100;
var_dump($value, round(round($value,1))); // float(97.5) float(98)
2+2=4
ахахахаха
Вы на каком языке пишите?
очень похоже на синтаксис русского =) но могу ошибаться =)
вы вообще знаете, как компьютер работает? я не говорю про вентили-шментили, а хотя бы как работа с числами примерно происходит?
web-разработчик — программист лишь с большой натяжкой
зависит от того, как туда попасть. тоже самое скоро можно будет сказать, например, про нэт.
но суть-то не в этом. неужели нельзя хотя бы ради интереса изучить этот вопрос?
я думаю, что программистов, можно разделить на три типа: те, которые начали изучение программирования с веба, на тех, которых через него «прошли» и тех, кто остановился на «вебе».
если программист желает развиться (даже в веб-программировании), то ему все-равно придется уходить из этой области в другие.
веб и, php в частности, слишком абстрактны, что бы требовать от программиста знание о экспоненциальной записи числа. а те, кому такие моменты интересны, рано или поздно займутся настоящим программированием.
0_o А веб-программирование разве не настоящее?
Как известно программисты бывают 10 типов: те, кто знает двоичное исчисление и те, кто не знает ©
Пока программирование делится на «веб-» и «не веб-», существуют те самые говнокодеры. Сам верстак и JS-кодер, но про представление чисел с плавающей точкой в курсе со школы ещё с паскаля. Очень печально, что сейчас учатся программировать на PHP, а не программировать…
Как известно программисты бывают 10 типов: те, кто знает двоичное исчисление и те, кто не знает ©

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

Тут нужно употреблять не программисты, а люди.
Да вот, похоже, приходится немного перефразировать под текущие реалии. Именно, что программисты.
Сейчас не все кто пишет — учатся.
Обижаете. Я раньше работал в институте кибернетики разрабатывал систему распознавания образов. А сейчас сайты делаю.
Если не секрет, что вас к этому сподвигло?
Мне это нравится жутко)
Не важно, как он работает. Проблема в том, что он работает неправильно.
Не соглашусь. Я считаю, что понимать, как оно там работает — самое важное. Потому что иначе ничего не остается, кроме как констатировать наличие проблемы.
А с сабжем вы встретитесь в любом яп'е.
Совершенно точно подмечено :)
Когда у нас во время разработки случается не предвиденный результат, мы друг другу говорим:
«Ты, же знаешь! PHP не врет» :)
Т.е. сам где-то накосячил, или не понимаешь как это должно рабоать.
Я не вижу ничего хорошего в том, что с сабжем я встречусь в любом яп'е. И ваше утверждение, если хорошо подумать, неразумно. Если как оно там работает — самое важное, то следуя Вашей логике, необходимо разобраться как работает процессор, выучить ассемблер и отрастить длинную-длинную бороду как у Кернигана и Ритчи. Звучит конечно очень красиво, но это глупость. PHP-программист должен писать web-приложения, а разработчик ЯП — устранять подобные ошибки с мантиссой. И будет именно так, потому что ЭТО разумно, а round(round($value,1)) — нет.
Я поясню свою мысль.
Нужно знать язык, на котором пишешь.
Данный топик — рассмотрен в PHP manual.
И это не баг мантиссы, а следствие работы с неточным типом.

А баги самого PHP встречаются гораздо реже, чем те же опечатки.
Ну да. Ещё разработчик ЯП должен устранять проблемы со сложностью алгоритмов, объёмом оперативной памяти, ОО проектированием и всем прочим, что PHP-программист не захотел выучить.

Вы готовы простить интерпретатор вообще за наличие округления? А то оно же в принципе не нужно. Ограниченность ресурсов ЭВМ — это низкоуровневые детали реализации, они не должны касаться PHP-программиста!

На досуге попытайтесь изобрести своё представление двоичного числа с плавающей точкой, в котором этой «ошибки» не будет. Это интересно, Вас ждёт ряд сюрпризов.
Если существует нечто под названием «BC Math», то это уже — способ представления двоичного числа с плавающей точкой, в котором этой «ошибки» не будет. Более того, сама строка «0.1» или «(9.975-9)*100» — это тоже еще один способ представления числа, лишенный погрешности, в котором искомое число записывается в виде последовательности двоичных чисел.

К счастью, изобретать свое представление двоичного числа с плавающей точкой я не буду, потому что складирование мёртвых нигеров — это не мой бизнес. Пусть лучше этим займутся разработчики ЯП — у них должно получиться в сто раз более качественное решение, чем то, до которого могу додуматься я, если только они не будут делать как Вы, утверждая что математическое выражение (97.5 != (9.975-9)*100) это даже и не ошибка.
Постарайтесь — как профессиональный разработчик на PHP — на досуге понять, почему представление чисел с плавающей точкой, допускающее погрешность, повсеместно используется. Вам будет интересно узнать, насколько SSE-инструкции Вашего процессора быстрее, чем это:

typedef enum {PLUS, MINUS} sign;

typedef struct bc_struct *bc_num;

typedef struct bc_struct
    {
      sign  n_sign;
      int   n_len;	/* The number of digits before the decimal point. */
      int   n_scale;	/* The number of digits after the decimal point. */
      int   n_refs;     /* The number of pointers to this number. */
      bc_num n_next;	/* Linked list for available list. */
      char *n_ptr;	/* The pointer to the actual storage.
			   If NULL, n_value points to the inside of
			   another number (bc_multiply...) and should
			   not be "freed." */
      char *n_value;	/* The number. Not zero char terminated.
			   May not point to the same place as n_ptr as
			   in the case of leading zeros generated. */
    } bc_struct;


Полюбуйтесь на свой BC Math. Если бы числа с плавающей точкой всегда представлялись в таком виде, мы бы до сих пор сидели в каменном веке.

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

Развитие вычислительной техники представляет собой непрерывную цепочку компромиссов — между производительностью и занимаемой памятью, между производительностью и точностью, между производительностью и ценой… Для того, чтобы обсуждать принятое однажды решение (например, пресловутый стандарт IEEE 754), нужно знать альтернативы этому решению, их достоинства и недостатки. Для этого и следует понимать основы архитектуры ЭВМ.

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

Ещё рекомендую прочесть local.joelonsoftware.com/mediawiki/index.php/Закон_Дырявых_Абстракций — Джоэл умнее меня, и язык у него подвешен лучше.
Сравнение чисел с плавающей запятой напрямую (а не их скажем разости) — моветон.
И источник ошибок (даже не потенциальный, а самый что ни на есть реальный).
C разностью тоже надо быть осторожным, я выше приводил пример:
0.1 + 0.1 + 0.1 - 0.3 = 5.6e-17 != 0

Ошибка появляется очень быстро:
0.1 + 0.1 - 0.1 = 0.10000000000000001
Имеется ввиду сравнение этих чисел через разность, с учётом погрешности:

e = 0.000001

a = 0.1 + 0.1 + 0.1 - 0.3
if( abs( a ) < e ) ...

a = 0.1 + 0.1 - 0.1
b = 0.1
if( abs(a - b) < e ) ...
Да, но константу придется подбирать по задаче…
C разностью тоже надо быть осторожным, я выше приводил пример:
0.1 + 0.1 + 0.1 — 0.3 = 5.6e-17 != 0

Вы опять напрямую сравниваете (!=)
Конечно. Именно, чтобы показать, что результат операции совсем другой, и «в лоб» здесь не сработает.
To everybody above:
Не, я согласен и знаю что флоат сравнивать плохо и т.п.

Просто почему-то был уверен что var_dump всегда показывает непременно точное значение :-/ Весь мой дебагинг, можно сказать, строится на этом как на трех китах, и тут такой облом. Простите за «юмор», это нервное — когнитивный диссонанс-с :)
Он показывает значение, а не машинное представление.
Надо же, ещё один человек узнал об этом. Ещё чуть-чуть и вы догадаетесь, вероятно, почитать о представлении чисел в языках программирования. Откроете для себя новый мир: узнаете с чем связан этот эффект, для чего нужен тип decimal в СУБД и JS2 и так далее. Завидую вам.

Для справки:
python -c 'print round((9.975-9)*100)'
97.0

Читайте руководство по языку, там многое описано. В том числе и то, с чем вы столкнулись.
Если кто читает PHP Internal Mailing list то там эту тему подняли, предложили патч и помоему его добавят в PHP 5.3. В следствии этого точность вычислений должна возрасти.
Собственно вот линкб помечено как Active: wiki.php.net/rfc/rounding
Что, числа будут храниться не в формате IEEE 754?
Хранится они будут так же как и хранились, а вот вычисления будут более точными. Для деталей читайте мною упомянутое RFC
Тьфу ты, округление собираются сделать более точным, извиняюсь за неточность.
Спасибо, прочитал.
НЛО прилетело и опубликовало эту надпись здесь
PHP тут не при чем. Так работает процессор с числами с плавающей точкой.
denver & Co. читайте bugs.php.net и PHP Internal Mailing list и будет вам счастье
Спасибо, вспомнилась именно эта статья, а тут раз — и ссылка на нее.
О, сколько нам открытий чудных… [C] Как говорили древние: RTFM.
Извечная проблема потери точности при работе с числами с плавающей точкой :)
Практически в любом языке.

Выход, где это _возможно_, использовать целочисленную арифметику.
Всегда можно представить 9.975 как 9975, а 97.5 как 97500 :)
А сравнивание чисел с плавающей точкой надо бы делать с учетом погрешности, например:
if (abs($a — $b) < 1e-8) { // true } else { // false }
а ceiling и floor для кого?
или обязательно нужен float? не очень ясно из поста, комментарии информативнее :)
Нужно было именно округление, а то бы конечно юзал ceil или floor.
Вот, примерно, как мы делали лет 5-7 назад в Pascal :)

function round2($a) {
  return round($a + 1e-8);
}

$value = 97.5;
var_dump($value, round2($value)); // float(97.5) float(98)
$value = (9.975-9)*100;
var_dump($value, round2($value)); // float(97.5) float(98)

А как вы определили константу 1e-8?
Попутно замечу:
round(0.49999999999) = 0 round2(0.49999999999) = 1
Опытным путем :))

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

Хотя, конечно там, где можно переписать на действия с целыми числами, я всегда за.
понятно :)
В принципе в языках бывают специальные типы данных для работы с десятичными числами. Например, в питоне есть тип Decimal с настраиваемой точностью. Думаю в пхп должен быть аналог. Либо можно использовать тот же BCmath
Не совсем уверен что топик стоило выносить на индекс, но раз вывели, то топик стоило логически завершить хотя бы заглянув на офф сайт php
ru.php.net/float

Тогда многих комментариев вроде
смею предположить, что в PHP реализовано статистическое округление


Просто бы не появилось и все остались бы со своими плюсами, а пользователи прочитавшие пост узнали что в php может случится и такой казус.
Знаете, что такое «коротко и лаконично»? 2 х 2 = 5. И это, кстати, доказуемо.
Это софизм.
Ну что же Вы, сударь?! Взяли и все испортили! Не все же это знали… Лишили людей еще одной головоломки на ближайшие рабочие сутки!
Тут о другом тема.
Тема то как раз о том же. Мне пришлось на многих языках программировать и с проблемами преобразования числовых типов данных знаком… Так вот вернемся к «2 х 2 = 5» — тут абсолютно аналогичная ситуация! Проблемы были, есть и будут есть!
Материалы по предмету «Программирование вычислений».
Касательно вопроса топика — Представление чисел в ЭВМ или В.И. Юров — Assembler (стр. 510-519). Последний вариант весит меньше, но при этом — интересная книга целиком :) Ну и википедия.
Так же вопрос освещён в местных материалах по ПВ, но где именно — уже не помню.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории