Ошибка с плавающей запятой в MATLAB - линейно разнесенные векторы [дубликаты]

ОС: Win 7 64bit. Матлаб: 2014а, 2015а

Когда я создаю вектор следующим образом:

a = 0.2:0.2:1

Я получил:

a = 0.2000    0.4000    0.6000    0.8000    1.0000

что ожидается. Теперь, когда я хочу увидеть, существует ли 0,6 в моем векторе, я набираю:

a == 0.6

и я получаю: 0 0 0 0 0

find(a == 0.6) также возвращает Empty matrix: 1-by-0

Это неожиданно. Все остальные значения он находит, а вот для 0.6 есть проблема. Я думаю, что хотя 0.6 создается, на самом деле это 0.6000000000000000001 или что-то подобное, что является проблемой. Вы можете видеть, что это так, по a > 0.6 и получите 0 0 1 1 1.

1-Во-первых, почему это происходит?

2-Во-вторых, можем ли мы увидеть все значение числа в Matlab, если да, то какая функция или настройка для этого?

Я создал такой же вектор с помощью linspace, но это тоже не помогло. Я нашел решение этой проблемы, набрав: a = roundn(a, -10). Однако я думаю, что такое решение даже не должно быть нужно в первую очередь.

3-Есть ли лучший способ заказать Matlab для получения точных значений?

Спасибо за вашу помощь.


person ilyas    schedule 26.04.2015    source источник
comment
Используйте символьную математику. Тип double не может точно представлять десятичные дроби.   -  person Ben Voigt    schedule 26.04.2015
comment
Добро пожаловать в программирование с плавающей запятой. См. также: Почему 24.0000 не равно 24.0000 в MATLAB? Попробуйте a = 0.2:0.2:1; a(3)-0.6.   -  person horchler    schedule 26.04.2015
comment
Спасибо вам, ребята. Итак, я вижу, что это проблема, но если это так, то это должно применяться к каждому значению. Это не относится к другим значениям в моем векторе. Только до 0,6. А если серьезно, то как мне сравнивать? Внезапно простое сравнение превратилось в громоздкое мероприятие.   -  person ilyas    schedule 27.04.2015


Ответы (1)


Во-первых, прочитайте документацию MATLAB по значениям с плавающей запятой, уделив особое внимание разделу об ошибках и точности с плавающей запятой: MATLAB с плавающей запятой

Вы столкнулись с невероятно распространенной проблемой с точностью вычислений с плавающей запятой. Важно понимать, что на самом деле вы не сравниваете:

>> a = 0.6;
>> b = 0.6;
>> a == b
   ans = 1

Вместо этого вы эффективно сравниваете:

>> a = 0.6;
>> b = 0.2 + 0.2 + 0.2;
>> a == b
   ans = 0

Причина очевидной логической ошибки здесь в том, что арифметика на самом деле не равна. Значения 0.6 и 0.2 представлены в виде чисел с плавающей запятой двойной точности к НАИБОЛЕЕ БЛИЖАЙШЕМУ возможному значению, разница известна как "ошибка с плавающей запятой".

Наблюдать за ошибкой просто:

>> a = 0.6;
>> b = 0.2 + 0.2 + 0.2;
>> a - b
   ans = -1.110223024625157e-16

Самое простое решение - использовать round() для вашего скаляра и вектора с одинаковой точностью, а затем выполнить сравнение:

>> a = 0.6;
>> b = [ 0.2 : 0.2 : 1 ];
>> roundn ( a , -10) == roundn ( b , -10 )
   ans = 0 0 1 0 0
person Juderb    schedule 26.04.2015
comment
Я понимаю. Я ценю вашу помощь. Но почему для других значений находит, а для 0,6 нет? Разве он не должен быть последовательным и не может найти и другие? Например, а == 0,8? - person ilyas; 26.04.2015
comment
Я не уверен, что ты имеешь в виду. Если вы сделаете a = 0.8;, b=0.2:0.2:1; и попробуете a==b, вы все равно получите ans = 0 0 0 0 0. - person Juderb; 26.04.2015
comment
Ну нет, я делаю то же, что и ты : 0 0 0 1 0 - person ilyas; 27.04.2015
comment
Исправление, я получаю тот же результат, что и вы. И в этом действительно есть смысл. eps(0.8) ans = 1.110223024625157e-16, eps(0.2) ans = 2.775557561562891e-17 и eps(0.2)*4 ans = 1.110223024625157e-16. По сути, 0,2 * 4 и 0,8 имеют одинаковую точность с плавающей запятой. - person Juderb; 27.04.2015
comment
Ух ты. Удивительно. Спасибо, что указали на это. Действительно eps(0.6) = 1.1102e-16 , eps(0.2)*3 = 8.3267e-17. Таким образом, для сравнения чисел самым безопасным способом является использование roundn? Это то, что вы делаете? Любое другое решение? - person ilyas; 27.04.2015
comment
По возможности старайтесь использовать целочисленные типы данных, так как при представлении целочисленного значения нет ошибки. В большинстве ситуаций это нецелесообразно, поэтому вы должны учитывать ошибку с плавающей запятой, и в этом случае roundn() является одним из вариантов. Альтернативой является использование достаточно близкого метода, такого как abs ( a - b ) <= eps ( a ) или что-то подобное. - person Juderb; 27.04.2015
comment
В порядке. Спасибо @Juderb. Также в приведенном выше примере вы показываете: a - b ans = -1.110223024625157e-16. Чтобы отобразить это, вы используете format long или что-то еще? - person ilyas; 27.04.2015
comment
Да, иначе он округляет отображаемое значение. - person Juderb; 27.04.2015