Delphi Math: почему 0,7 <0,70? - PullRequest
       23

Delphi Math: почему 0,7 <0,70?

10 голосов
/ 02 мая 2010

Если у меня есть переменные a, b, a c типа double, пусть c: = a / b и задают значения a и b 7 и 10, то значение c 0,7 регистрируется как LESS THAN 0,70.

С другой стороны, если все переменные имеют расширенный тип, то значение с 0, равное 0,7, не регистрируется как менее 0,70.

Это кажется странным. Какую информацию мне не хватает?

Ответы [ 5 ]

18 голосов
/ 02 мая 2010

Во-первых, необходимо отметить, что литералы с плавающей точкой в ​​Delphi имеют тип Extended. Поэтому, когда вы сравниваете double с литералом, double, вероятно, сначала «расширяется» до Extended, а затем сравнивается. (Редактировать: это верно только в 32-битном приложении. В 64-битном приложении Extended является псевдонимом Double)

Здесь будут отображаться все сообщения ShowMessage.

procedure DoSomething;
var
  A, B : Double;
begin
  A := 7/10;
  B := 0.7; //Here, we lower the precision of "0.7" to double

  //Here, A is expanded to Extended...  But it has already lost precision. This is (kind of) similar to doing Round(0.7) <> 0.7
  if A <> 0.7 then 
    ShowMessage('Weird');

  if A = B then //Here it would work correctly.
    ShowMessage('Ok...');

  //Still... the best way to go...
  if SameValue(A, 0.7, 0.0001) then
    ShowMessage('That will never fails you');
end;

Вот немного литературы для вас

Что должен знать каждый компьютерщик об арифметике с плавающей точкой

11 голосов
/ 02 мая 2010

Нет представления для математического числа 0.7 в двоичной переменной с плавающей точкой. Ваше утверждение вычисляется в c ближайшем double, что (согласно тому, что вы говорите, я не проверял) немного ниже 0,7.

По-видимому, в расширенной точности ближайшее число с плавающей точкой к 0,7 немного выше его. Но пока нет точного представления о 0,7. В двоичной с плавающей запятой нет никакой точности.

Как правило, любое нецелое число, последнее ненулевое десятичное число которого не равно 5, не может быть представлено точно как двоичное число с плавающей запятой (обратное утверждение неверно: 0.05 также не может быть представлено точно). 1008 *

8 голосов
/ 02 мая 2010

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

Взять, например, 2/3. Он не может быть представлен точно в десятичном виде. С 4 значащими цифрами он будет представлен как 0,6667. С 8 значащими цифрами это будет 0,66666667. Трейлинг 7 является сводкой, отражающей, что следующая цифра будет> 5, если есть место для ее хранения.

0,6667 больше, чем 0,66666667, поэтому компьютер оценит 2/3 (4 цифры)> 2/3 (8 цифр).

То же самое верно для ваших .7 против .70 в двойных и расширенных переменных.

Чтобы избежать этой конкретной проблемы, попробуйте использовать один и тот же числовой тип в коде. При работе с числами с плавающей запятой в общем, есть много мелких вещей, которые вы должны остерегаться. Самое главное - не писать свой код, чтобы сравнить два числа с плавающей точкой на равенство - даже если они должны иметь одинаковое значение, в вычислениях есть много факторов, которые могут привести к тому, что они окажутся совсем немного иначе. Вместо сравнения на равенство нужно проверить, что разница между двумя числами очень мала. Насколько малой должна быть разница, зависит от вас и характера ваших вычислений, и обычно оно называется эпсилоном, взятым из теоремы и доказательства исчисления.

5 голосов
/ 02 мая 2010

Тебе не хватает Эта вещь .

См., В частности, главу « Проблемы точности ». Смотрите также ответ Паскаля. Чтобы исправить свой код без использования типа Extended, необходимо добавить модуль Math и использовать оттуда функцию SameValue, специально созданную для этой цели.

Обязательно используйте значение Epsilon, отличное от 0, когда вы используете SameValue в вашем случае.

Например:

var
  a, b, c: double;


begin
  a:=7; b:=10;
  c:=a/b;

  if SameValue(c, 0.70, 0.001) then
    ShowMessage('Ok')
  else
    ShowMessage('Wrong!');
end;

НТН

1 голос
/ 03 мая 2010

Посмотрите на эту прекрасную статью о Delphi и числах с плавающей запятой - она ​​должна что-нибудь объяснить: http://rvelthuis.de/articles/articles-floats.html

...