2

Каким будет результат выполнения следующего фрагмента кода:

<?
echo floor((0.1+0.7)*10);
?>

И почему?

alex_90
  • 1,262

4 Answers4

5

Будет именно 7. Потому что (0.1 + 0.7) * 10 не будет 8, а будет 7.99(тут дальше будут любые цифры). А так как это число будет меньше 8, то результат будет 7. Почему? особенности сложение вещественных чисел.

обновление для @AlexWindHope и всех других, кто обвинил меня в незнании математики

Почитайте на досуге стандарт IEEE 754 и поймите, что дробные числа не всегда могут быть представлены точно. Здесь http://www.h-schmidt.net/FloatApplet/IEEE754.html есть хороший апплет, который показывает, как именно выглядит число в внутри, в памяти. В частности:

0.1 = 0.1000000014 9011612
0.2 = 0.2000000029 8023224
0.7 = 0.6999999880 7907100

Просуммировав все аккуратно имеем

0.1 + 0.7 = 0.79999998966918712
0.2 + 0.7 = 0.89999999105930324

То есть видим, хотя числа и близкие к желаемым, но все таки не равны им. Я правда не нашел, как реализована в пхп функция floor, думаю оно бы прояснило, почему первому не хватило, а второму уже хватило, что бы перейти через разряд.

Никогда не путайте школьную математику и процессорную.

еще одно обновление:

Если пойти на сайт php и почитать, что пишут о типе float, то как раз там и приводят пример, данный вопрошающим:). Всем новичкам читать до полного понимания.

KoVadim
  • 112,121
  • 6
  • 94
  • 160
  • А можно источник где вы прочитали про 7.9999999999 ? – alex_90 Dec 06 '11 at 15:58
  • точно! правильно! – Maksym Prus Dec 06 '11 at 16:00
  • Это базовые знания. php тут не при чем. Испытайте такую прогу
    <?php
    if ((0.1 + 0.7)*10 < 8) {
       print("less 8");
    } else {
       print ("8 or more");
    }
    ?>
    
    

    Хотя возможно, что на некоторых платформах будет 8.

    – KoVadim Dec 06 '11 at 16:03
  • (0.1 + 0.7) * 10

    Будет 7.99? У вас по математике что было?xD

    Ну сделайте

     echo (0.1 + 0.7) * 10;
    
    –  Dec 06 '11 at 16:03
  • При этом
    floor((0.7+0.2)*10) == 9; // О_о
    
    
    floor((0.7+0.1)*10) == 7; // ну бред ппц
    
    –  Dec 06 '11 at 16:07
  • Еще
    floor((0.7+0.15)*10) == 8; // !!!
    
    – alex_90 Dec 06 '11 at 16:09
  • Нашел похожие чудеса в комментариях на офф.сайте php ^.^
    $int = 0.99999999999999999; // 17 чисел после точки 
    echo floor($int); // -> 1
    
    

    and

    $int = 0.9999999999999999; // 16 чисел
    echo floor($int); // -> 0 
    
    

    моя плакать xD

    echo floor((0.1+0.7000000000000001)*10); // -> 8 xDDDDDD
    
    

    Если это особенность сложения вещщественных чисел, то почему работает. например с (0.7+0.2)*10?

    –  Dec 06 '11 at 16:16
  • @AlexWindHope, скоре всего это и будет ответ. Можете преобразовать в ответ и добавить ссылку откуда вы взяли материал? – alex_90 Dec 06 '11 at 16:23
2

Это действительно проблема сложение вещественных чисел, но мне всеравно непонятно почему тогда floor работает например с 0.7+0.2, поидее тут должно выводить 8...

Ну да собственно не суть, чтобы работало и не было таких вот чудес надо писать так:


                    echo floor(round(0.7+0.1, 1)*10); // -> 8

PS: примеры в комментах смотрел тут

UPD Насколько я понимаю здесь все намного сложнее... И мне кажется что это "плоское место" реализации функции floor, дабы не быть голословным и уж точно разрушить то, что написал @KoVadim приведу несколько примеров:

floor((0.0+0.1)*10); // -> 1
floor((0.1+0.1)*10); // -> 2
floor((0.2+0.1)*10); // -> 3
floor((0.3+0.1)*10); // -> 4
floor((0.4+0.1)*10); // -> 5
floor((0.5+0.1)*10); // -> 6
floor((0.6+0.1)*10); // -> 7
floor((0.7+0.1)*10); // -> 7
floor((0.8+0.1)*10); // -> 9

@KoVadim - да, вы правы, протестил для 4.0 - фейл :) Но в данном случае round($num, 1) - решает эту проблему

PS: round(), в данном случае, я использовал именно для решения конкретной задачи

  • А как в PHP строятся графики? Возможно это бы ускорило решение проблемы. – alex_90 Dec 06 '11 at 16:35
  • не совсем понял - о каких графиках речь? –  Dec 06 '11 at 16:37
  • Как в математике. На координатной плоскости. В идеале должны получиться "ступеньки". – alex_90 Dec 06 '11 at 16:40
  • Создавайте отдельный вопрос, надо видеть ваш код и задачу в целом –  Dec 06 '11 at 16:42
  • вот за код вида floor(round(0.7+0.1, 1)*10) нужно наказывать. Да в этом случае результат совпадет с придуманным (не ожидаемым, а именно придуманным). А если будем складывать не 0.7 и 0.1, а 0.7 и 0.18, тогда код с округлением будет делать совсем не то. – KoVadim Dec 06 '11 at 17:08
  • Уважаемый - у вас есть другие предложения? ну так чего вы медлите - просвятите нас –  Dec 06 '11 at 17:33
  • Как разруливать данную ситуацию? все зависит от того, что Вы хотите получить и какую задачу решаете. В обыденной программистской жизни редко складываются числа явно. – KoVadim Dec 06 '11 at 17:44
  • Обновил ответ, специально для вас :) –  Dec 06 '11 at 17:49
  • А давайте для затравки посмотрим ещё на вывод в диапазоне 0.1 + 4 до 0.1 + 6. Там тоже не все чисто:) И я не говорил, что это применимо только для php. Это применимо для любого языка, который не делает программной симуляции дробных чисел. – KoVadim Dec 06 '11 at 18:35
0

Округление до меньшего значения

0.1+0.7 = 0,8*10 = 8.

А floor(8) = 8

Nicolas Chabanovsky
  • 51,426
  • 87
  • 267
  • 507
Maksym Prus
  • 2,884
0

Ну чего же здесь непонятного? Язык использует не десятичные, а "двоичные" дроби. Вот если заменить 0.1 на 3/32 или на 25/256, и 0.7 заменить на что-то подобное, то результат получится совершенно предсказуемым.

BuilderC
  • 2,850