Пред. тема | След. тема |
Автор | Сообщение | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
Постоялец форума Сообщения:174 | Сколько будет (0,3 - 0,01 ) / 0,01 ? Ответ: 28... Так сработала функция Trunc(). И именно при 0,3 и 0,01! Вычисление (0,2 - 0,01) / 0,01 даёт 19, т.е. верный результат. | |||||||||
10 дек 2011, 21:33 | ||||||||||
Постоялец форума Сообщения:174 | Вот ассемблерный код реализации встроенной ф-ии Trunc() из Delphi 7
Может, кто по упражняется в поиске причины ошибки? | |||||||||
13 дек 2011, 23:58 | ||||||||||
Эксперт Сообщения:4847 Откуда:Томск | Чует мое сердце, что тут программистов Delphi нема... А багов в компиляторах много. Взять ту же самую Java, в которой регулярно находят интересные места. | |||||||||
14 дек 2011, 01:17 | ||||||||||
Постоялец форума Сообщения:174 | Пока подозрение падает на то, что при вычислении числа с плавающей точкой (0,3 - 0,01) / 0,01 получается не 29, а 28,99999999..., и функция Trunc() благополучно съедает девятки... Если брать не 0,3, а 0,2, что меньше, то вычисление (0,2 - 0,01) / 0,01 даёт 19,000... Пока как-то так. | |||||||||
14 дек 2011, 22:17 | ||||||||||
Постоялец форума Сообщения:174 | Переписал функцию так, что в Trunc() передаётся не выражение, а вспомогательная переменная N:
Теперь всё работает. А не работало как надо потому, что в функцию Trunc() по правилам передаётся Extended, т.е. 10 байтное число, с которым работает процессор (FPU). Написав N := ..., мы тем самым округляем число Extended до Double (8 байт). А затем передаём в функцию Trunc() как Extended с добавочными нулями. Физически FPU из регистра ST(0) 10 байтное число передаёт (по своим правилам округления) в стек как 8 байтный Double. Т.е. даже если и вычислил процессор 28,999999999999, то в стек он передаст 29, т.к. 8 байт хватает только на 15-16 знаков после запятой... Процессор может вычислить 28,999999999999 вместо точного 29, т.к., например, десятичное число 0,01 ну никак нельзя представить с помощью конечного числа бит. 0,5 можно: это 0,1, т.е. 2^0 + 2^-1. А 0,01 = 0,000000101001... = 2^-7 + 2^-9 + 2^-12 + ... Если мы напишем Trunc((_stop - _start) / _step), то вычитание и деление выполняются с типом Extended, а результат сразу же передаётся в функцию Trunc() - вернее, она сразу же и вызывается, выполняя операции над числом-результатом деления, которое хранится в 10 байтном регистре FPU ST(0). В последнем случае результат получается несколько быстрее, но с небольшим таким багом. Для некоторых задач не важно, сколько у вас отсчётов: подумаешь 999 вместо тысячи. Но о такой тонкости всегда следует помнить. Так что это не "баг Делфи", а следствие ограниченной разрядности вещественных чисел, хранимых в процессоре. | |||||||||
15 дек 2011, 00:10 | ||||||||||
Постоялец форума Сообщения:174 | Для примера можно зайти на сайт и посмотреть как представлены числа в процессоре http://www.h-schmidt.net/FloatApplet/IEEE754.html Можно ввести 0,3, закодировать его 32 битами (4 байтами, одинарная точность) и получить 0,30000001192092896 в 64-битном представлении (8 байт, двойная точность). Это так же, как и при попытке представить одну треть в виде конечного десятичного числа 0,3333333... Одну вторую можно - это 0,5. Десятичное представление имеет форму sign*a*10^k, где sign - знак, a - мантисса, 2.0 > a > 1.0 k - экспонента. Двоичное представление (процессорное) - по аналогии sign*a*2^k, т.е. 0,3 представится как +1,2*2^-2. Т.о., экспонента (в данном случае это -2) передаётся с помощью конечного набора бит точно, знак - точно, а мантисса - далеко не всегда. Точно - при условии, что вещественные числа лежат в заданных пределах (2^-127 ... 2^128). 1,2 представляется в двоичном виде (одинарная точность) как 00110011001100110011010, т.е. 2^-3 + 2^-4 + 2^-7 + 2^-8 + 2^-11 + 2^-12 + 2^-15 + 2^-16 + 2^-19 + 2^-20 + 2^-22 = 838861/4194304 = 0,2000000476837158203125 точно, что отличается от 0,2 в 8 знаке после запятой (этому и соответствует тип "одинарная точность"). | |||||||||
15 дек 2011, 23:56 | ||||||||||
Страница 1 из 1 | [ Сообщений: 6 ] |
Кто сейчас на конференции |
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6 |
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы можете добавлять вложения |