[Решено] Странный результат

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

[Решено] Странный результат

Сообщение NickZane » 13.11.2011 18:38:02

Код: Выделить всё
a,b,c:real;
...
a:=0.05;
b:=0.9;
c:=a+b;
writeln(c=0.95)

Оператор writeln выдаёт false. Почему?
Ну после c:=a+b с становится равным 0.95 и writeln должен выдать true. Или я чегото не понимаю?
NickZane
незнакомец
 
Сообщения: 2
Зарегистрирован: 13.11.2011 18:29:22

Re: Странный результат

Сообщение teapot » 13.11.2011 19:09:02

Запустите в режиме отладки и посмотрите на знаки после запятой. Почему freepascal так считает вещественные числа, мне самому интересно - я в этом деле новичок.
Аватара пользователя
teapot
незнакомец
 
Сообщения: 7
Зарегистрирован: 13.11.2011 15:20:23

Re: Странный результат

Сообщение Mr.Smart » 13.11.2011 19:44:46

teapot по оной простой причине. Все вещественные константы в FPC (архитектура i386) соответствуют типу Extended (10 байт), а в данном случае Real имеет другое представление и при сравнение роисходит преобразование типов. При вещественном преобразовании частенько происходит не соответствие в дробной части.
Можете сами убедиться:
Код: Выделить всё
writeln(SizeOf(Real));
writeln(SizeOf(0.95));
writeln(SizeOf(Extended));


Добавлено спустя 1 час 32 минуты 14 секунд:
на правах К.О. добавлю (может никто не понял). Данная вещь будет работать если:
Код: Выделить всё
var
  a,b,c: Extended;



:wink:

Добавлено спустя 1 минуту 6 секунд:
или
Код: Выделить всё
writeln(c=Real(0.95))
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Странный результат

Сообщение teapot » 13.11.2011 22:20:44

Да, хотя я и не автор, приблизительно ясно. Только непонятно, происходит преобразование типов при сравнении, как Вы сказали, или уже при присвоении. И, кстати, почему тогда эти последние два байта не заполняются принудительно нулями? Или это вопрос истории?

Добавлено спустя 1 минуту 20 секунд:
Простите, я не совсем понимаю, как работает компьютер. Для меня это волшебная коробочка, которая позволяет общаться с друзьями вконтакте.
Аватара пользователя
teapot
незнакомец
 
Сообщения: 7
Зарегистрирован: 13.11.2011 15:20:23

Re: Странный результат

Сообщение NickZane » 13.11.2011 22:30:23

Проблемка шире. Я здесь лишь более простой пример привел. А вообще есть задача в которой для функции в цикле производится перебор аргументов X: [0.05; 0.95]. А шаг приращения h как раз равен 0.9 и, соответственно, должны выдаваться результаты для f(0.05) и f(0.95). Условием выхода из цикла стоит X<=0.95.

x:=0.05;
while x<=0.95 do
begin
...
x:=x+0.9;
end;

цикл должен выполниться два раза (для х=0.05 и для х=0.95), а он зараза :) выполняется один раз!
То что здесь чтото происходит с соотношением вещественных типов я сразу понял, но незнал как это обойти. Кстати, если использовать Real(0.95) цикл всё равно один раз выполняется. Но я решил проблемку так: while single(x)<=single(0.95) do ... и всё заработало как надо. Спасибо большое за ответы! Особенно Mr.Smart"у.
NickZane
незнакомец
 
Сообщения: 2
Зарегистрирован: 13.11.2011 18:29:22

Re: Странный результат

Сообщение informat » 14.11.2011 06:31:38

NickZane писал(а):
Код: Выделить всё
a,b,c:real;
...
a:=0.05;
b:=0.9;
c:=a+b;
writeln(c=0.95)

Оператор writeln выдаёт false. Почему?
Ну после c:=a+b с становится равным 0.95 и writeln должен выдать true. Или я чегото не понимаю?


Действительные числа - ПРИБЛИЗИТЕЛЬНЫЕ. И сравнивать их на равенство бесполезно.
2.0*2.0 = 4.0 может быть НЕ верно.
действительные числа A и B нельзя сравнивать на равенство. Сравнивать можно только приблизительно.
Вместо
A=B
нужно писать
abs(A-B)<0.0001 или с какой точностью нужно.
Аватара пользователя
informat
новенький
 
Сообщения: 62
Зарегистрирован: 27.10.2010 09:44:20
Откуда: http://informat.name

Re: Странный результат

Сообщение Vadim » 14.11.2011 08:27:49

NickZane
Есть специальный тип чисел - бухгалтерский (Currency), призванный решать подобные коллизии. Если знаков после запятой у Вас не более четырёх, то можно использовать этот тип. При этом все сравнения между переменными этого типа проходят корректно.
Код: Выделить всё
Var
  c1, c2: Currency;

Begin
  c1:=0.95;
  c2:=0.95;
  WriteLn(c1=c2);  //Будет TRUE
  c2:=0.951;
  WriteLn(c1=c2);  //Будет FALSE
End.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Странный результат

Сообщение SAK » 14.11.2011 11:48:28

NickZane писал(а):
Код: Выделить всё
x:=0.05;
while x<=0.95 do
begin
    ...
    x:=x+0.9;
end;

Почему не написать "while x<1 do" ? Это будет надёжнее чем "while single(x)<=single(0.95) do" потому как при математичестких операциях 0.95 может превратиться в 0.94999999999999... или 0.95000000000001 или что-то подобное.
SAK
постоялец
 
Сообщения: 158
Зарегистрирован: 18.02.2006 00:45:14
Откуда: Тим

Re: Странный результат

Сообщение and » 27.11.2011 18:37:44

Хех... И снова старые-старые грабли.
Дело не в FPC и даже не в single vs extended. Все числа в "волшебной коробочке" представляются в виде степеней двойки (стандарт IEEE 754). Из чего следует, что не любая конечная десятичная (т.е. сделанная из степеней десятки) дробь представима в виде конечной двоичной. Так, 0.5=2^(-1) - всё OK. А 0.1 точного представления не имеет.
В Нете много инфы по этому вопросу. Например, вот здесь простым понятным языком.
Аватара пользователя
and
постоялец
 
Сообщения: 124
Зарегистрирован: 16.09.2009 17:11:01
Откуда: г. Гомель, Беларусь


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6

Рейтинг@Mail.ru