Я вычислил эту простую сумму на Matlab:
2*0.04-0.5*0.4^2 = -1.387778780781446e-017
но результат не равен нулю. Что я могу сделать?
У Аабаза и Джима Клея есть хорошие объяснения того, что происходит.
It's often the case that, rather than exactly calculating the value of 2*0.04 - 0.5*0.4^2, what you really want is to check whether 2*0.04 and 0.5*0.4^2 differ by an amount that is small enough to be within the relevant numerical precision. If that's the case, than rather than checking whether 2*0.04 - 0.5*0.4^2 == 0
, you can check whether abs(2*0.04 - 0.5*0.4^2) < thresh
. Here thresh
can either be some arbitrary smallish number, or an expression involving eps
, which gives the precision of the numerical type you're working with.
РЕДАКТИРОВАТЬ: Спасибо Джим и Таль за предлагаемое улучшение. Изменено, чтобы сравнить абсолютное значение разности с порогом, а не разницу.
Matlab использует числа с плавающей запятой с двойной точностью для хранения реальных чисел. Это числа формы m * 2 ^ e
, где m
- целое число между 2 ^ 52
и 2 ^ 53
( mantissa ) и e
является показателем. Давайте назовем число числом с плавающей запятой, если оно имеет эту форму.
Все числа, используемые в вычислениях, должны быть числами с плавающей запятой. Часто это можно сделать точно, как в 2
и 0.5
в вашем выражении. Но для других чисел, в первую очередь большинства чисел с цифрами после десятичной точки, это невозможно, и нужно использовать аппроксимацию. Что происходит в этом случае, так это то, что число округлено до ближайшего числа с плавающей запятой.
So, whenever you write something like 0.04
in Matlab, you're really saying "Get me the floating-point number that is closest to 0.04
. In your expression, there are 2 numbers that need to be approximated: 0.04
and 0.4
.
Кроме того, точный результат операций, таких как сложение и умножение чисел с плавающей запятой, может не быть числом с плавающей запятой. Хотя он всегда имеет вид m * 2 ^ e
, мантисса может быть слишком большой. Таким образом, вы получаете дополнительную ошибку от округления результатов операций.
В конце дня простое выражение, подобное вашему, будет примерно на 2 ^ -52 раза больше размера операндов или около 10 ^ -17.
Вкратце: причина, по которой ваше выражение не оценивается в ноль, двояка:
Я не знаю, применима ли она к вашей проблеме, но зачастую самым простым решением является масштабирование ваших данных.
Например:
a=0.04;
b=0.2;
a-0.2*b
ans=-6.9389e-018
c=a/min(abs([a b]));
d=b/min(abs([a b]));
c-0.2*d
ans=0
EDIT: of course I did not mean to give a universal solution to these kind of problems but it is still a good practice that can make you avoid a few problems in numerical computation (curve fitting, etc ...). See Jim Clay's answer for the reason why you are experiencing these problems.
То, что вы видите, это ошибка квантования . Matlab использует двойники для представления чисел, и, хотя они способны к большой точности, они все равно не могут представлять все реальные числа, потому что существует бесконечное количество действительных чисел. Я не уверен в трюке с Аабазом, но в целом я бы сказал, что вы ничего не можете сделать, кроме того, что вы можете массировать свои входы, чтобы быть двойными.
Я уверен, что это случай, когда вы стареете с точки зрения точности с плавающей запятой.
Вам нужна точность 1е-17? Это всего лишь случай желания «хорошенького» вывода? В этом случае вы можете просто использовать форматированный sprintf для отображения количества значимых цифр, которые вы хотите.
Поймите, что это не проблема Matlab, а фундаментальное ограничение того, как числа представлены в двоичном формате.
Для удовольствия, выработайте то, что .1 находится в двоичном формате ...
Некоторые ссылки: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems http://www.mathworks.com/support/tech-notes/1100/1108.html