Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Алгоритмы > Странности при подсчете чисел..... |
Автор: Wowa 11.2.2004, 02:07 |
Создал я значит цикл: for ($i = 7.12; $i <= 10; $i=$i+0.01) { ... } При его выполнении выясняется, что компьютер прибавляет к $i 0.01 при каждом проходе цикла, но иногда компьюет прибавляет не 0.01, а меньшее число. У меня на компьютере комп досчитал до: $i=9.51 и следуюшее значение выдало $i=9.5199999999999 Далее: 9.5299999999999 9.5399999999999 .... С чем это связано, как объясняется и как с этим бороться? |
Автор: Wowa 11.2.2004, 02:09 |
Запусти я этот код на другом компьютере - результат такой же. |
Автор: cardinal 11.2.2004, 02:31 | ||||||
Компьютер не ошибается в этом случае, что подтверждает тот факт, что как ты сам сказал второй компер ведет себя точно также.
С бинарной арифметикой.
Объясняется тем, что бинарный система счисления не позволяет выражать дроби. Был бы я в Си, я бы сделал листинг на asm и тогда делал бы последующие выводы, т.к. не уверен чем реализована работа с дробными числами - предполагаю, что этим занимается coprocessor. Но все равно вопрос остается, что с твоим кодом делает compiler.
Если это такой одиночный случай в проге, то я бы сделал так: for (i = 712; i <= 1000; i++) { ... } ну и в момент надобности делал бы операцию div 100 и mod 100 получая таким образом целую часть и дробную. А если это не одиночный случай, то я бы сделал тоже самое несколько раз потому, что ничего более полезного в голову не приходит. ![]() p.s. Уже даже Admin нарушает правила форума! Аж три вопроса в раз: "с чем связано?", "как объясняется?" и "как с этим бороться?". ![]() |
Автор: maxim1000 11.2.2004, 12:27 | ||
бинарная система кроме основания ничем не отличается от десятичной и позволяет выражать дроби причина в том, что одни и теже дроби в одной системе могут иметь простой вид, а в другой становиться периодическими а периодические дроби компьютер усекает (независимо от того, как там реализована арифметика) |
Автор: Wowa 12.2.2004, 22:23 |
Я сделал цикл: for ($k = 712; $k <= 1000; $k++) { $i=$k/100; ... } Теперь похоже все Ок. Вычисляется все точно. |
Автор: val 13.2.2004, 10:39 | ||
Да, эта тема обсуждалась в рубрике С/С++, действительно двоичная арифметика имеет такие ньансы при работе с действительными числами:
|
Автор: OlegsDP 25.2.2004, 14:28 | ||
Можно побаловаться с таким myprog.cpp:
|
Автор: Waters 29.2.2004, 18:15 |
И в чем смысл "баловства"? ![]() |
Автор: OlegsDP 1.3.2004, 17:01 |
Можно найти такие два числа, которые "не складываются": myprog 1 0.000000000000001 AGA! т.е. "сложились" myprog 1 0.0000000000000001 OGO! т.е. "не сложились" |
Автор: PILOT 6.3.2004, 15:04 |
Это связано именно с двоичной арифметикой, хотя если быть точнее то с представлением float... число float в простом случае (я уменьшил число байт для простоты) имеет формат: SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM где: S - это знак E - Экспонента (-127...+128) M - 24бита Мантиссы (на самом деле 23) И число представляется в виде: ±mantissa × 2^exponent Например число: -12.5 будет представлено в виде: 0xC1480000. Как так получилось? Разбираемся: формат числа: SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM в двоичном коде: 11000001 01001000 00000000 00000000 шестнадцатиричный код: 00 00 48 C1 Знак = 1 следовательно это отриц. число. Значение Экспоненты 10000010b или 130d. Вычитаем 127 (это наше смещение нуля) из 130 получаем 3, это и есть Экспонента. Видно, что Мантисса выражается числом: 10010000000000000000000b считается, что перед мантиссой всегда есть единица, в этоге получаем следующее число в двоичном представлении: 1.10010000000000000000000b Теперь учитываем экспоненту, т.к. она равна трем, то точка сдвинется на три разряда вправо: 1100.10000000000000000000b это число и есть двоичное представление -12.5. Проверяем (целая часть): (1 × 2^3) + (1 × 2^2) + (0 × 2^1) + (0 × 2^0)=12 (учитвая знак: -12) теперь дробная часть: 10000000000000000000b (1 × 2^-1) + (0 × 2^-2) + (0 × 2^-3) + … = 0.5 прибавляем: -(12+0.5)=-12.5 теперь не трудно сообразить, что 0.01 представляется сложнее в мантиссе, чем -12.5, потому что 0.01 это не .0625 или .125. А для представления 0.01 придется использовать все биты мантиссы (т.е. точности не хватит), так или иначе набежит один значащий бит в мантиссе, либо в плюс либо в минус. СУВ. |