Если у меня есть переменные a, b, c типа дважды, позвольте c: = a/b, и дают a и b значения 7 и 10, затем значение c 0,7 регистров, как являющихся меньше чем 0,70.
С другой стороны, если переменные являются всем расширенным типом, то значение c 0,7 не регистрируется как являющийся меньше чем 0,70.
Это кажется странным. Какую информацию я пропускаю?
Математическое число 0,7
не представлено в двоичном формате с плавающей запятой. Ваш оператор вычисляет в c
ближайший double
, который (согласно тому, что вы говорите, я не проверял) немного ниже 0,7.
По-видимому, в расширенной точности ближайшее к 0,7 число с плавающей запятой немного выше него. Но до сих пор нет точного представления для 0.7. В двоичных числах с плавающей запятой нет никакой точности.
Как показывает практика, любыецелое число, последнее ненулевое десятичное число которого не равно 5, не может быть представлено точно как двоичное число с плавающей запятой (обратное неверно: 0,05 также не может быть представлен точно).
Во-первых, необходимо отметить, что литералы с плавающей запятой в 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;
Вот вам литература
Что должен знать каждый компьютерный ученый о плавающих точках Арифметика
Взгляните на эту отличную статью о Delphi и числах с плавающей запятой - она должна объяснить все: http://rvelthuis.de/articles/articles- floats.html
Это связано с количеством разрядов точности в двух различных типах плавающей точки, которые вы используете, и с тем фактом, что многие числа не могут быть представлены точно, независимо от точности. (С точки зрения чистой математики: иррациональные числа превосходят рациональные)
Возьмем, к примеру, 2/3. Его нельзя точно представить в десятичной системе счисления. С 4 значащими цифрами оно будет представлено как 0,6667. С 8 значащими цифрами это будет 0,66666667. Следующая цифра 7 является округлением, отражающим, что следующая цифра была бы > 5, если бы было место для ее сохранения.
0,6667 больше, чем 0,66666667, поэтому компьютер оценит 2/3 (4 цифры) > 2/3 (8 цифр).
То же самое верно и в отношении .7 против .70 в двойных и расширенных переменных.
Чтобы избежать этой специфической проблемы, старайтесь использовать один и тот же числовой тип во всем коде. При работе с числами с плавающей запятой вообще есть много мелочей, за которыми нужно следить. Самое главное - не писать код для сравнения двух плавающих чисел на равенство - даже если они должны быть одинаковыми, существует множество факторов в вычислениях, которые могут привести к тому, что в итоге они будут отличаться совсем немного. Вместо сравнения на равенство, вам нужно проверить, что разница между двумя числами очень мала. Насколько мала должна быть разница, зависит от вас и от характера ваших вычислений, и обычно ее называют эпсилоном, взятым из теоремы и доказательства.
Вам не хватает этой штуки.
См. особенно главу 'Проблемы точности'. См. также ответ Паскаля.
Чтобы исправить ваш код без использования типа 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