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