Математика Delphi: Почему 0.7 <0.70?

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

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

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

10
задан Jeroen Wiert Pluimers 3 May 2010 в 08:25
поделиться

5 ответов

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

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

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

11
ответ дан 3 December 2019 в 13:40
поделиться

Во-первых, необходимо отметить, что литералы с плавающей запятой в Delphi относятся к расширенному типу. Поэтому, когда вы сравниваете двойное значение с литералом, оно, вероятно, сначала «расширяется» до расширенного, а затем сравнивается. (Правка: это верно только в 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;

Вот вам литература

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

18
ответ дан 3 December 2019 в 13:40
поделиться

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

1
ответ дан 3 December 2019 в 13:40
поделиться

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

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

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

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

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

8
ответ дан 3 December 2019 в 13:40
поделиться

Вам не хватает этой штуки.

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

При использовании SameValue в вашем случае обязательно используйте значение Epsilon, отличное от 0.

For example:

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;

HTH

5
ответ дан 3 December 2019 в 13:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: