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

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

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

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

Ответ 1

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

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

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

Ответ 2

Во-первых, необходимо отметить, что литералы float в 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;

Вот какая литература для вас

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

Ответ 3

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

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

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

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

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

Ответ 4

Вам не хватает This Thing.

См. раздел " Точность. См. Также ответ Паскаля. Чтобы исправить код без использования типа 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;

НТН

Ответ 5

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