MatLab — арифметика с переменной точностью

У меня есть небольшой вопрос относительно команды vpa, которую можно использовать для вычисления символьных выражений в MatLab.

В моем учебнике написано следующее:

«Вы должны быть осторожны, когда используете такие функции, как sqrt для чисел, которые по умолчанию приводят к числу с плавающей запятой двойной точности. Вам нужно передать такой ввод в vpa в виде символьной строки для правильной оценки: vpa('sqrt(5)/pi')».

Я не совсем понимаю жаргон здесь. Почему для большинства входных данных я получаю один и тот же ответ, ввожу ли я vpa(input) или vpa('input'), но не для квадратных корней? Например, если я наберу vpa(sin(pi/4)) или vpa('sin(pi/4)'), я получу точно такие же ответы, но если я наберу приведенную выше задачу как vpa(sqrt(5)/pi), я не получу тот же ответ, что и при вводе vpa('sqrt(5)/pi').

Если бы кто-то мог объяснить это немного подробнее, чем то, что делает моя книга выше, я был бы очень признателен!


person Kristian    schedule 03.06.2012    source источник
comment
вам может быть интересно прочитать этот вопрос: В MATLAB переменные ДЕЙСТВИТЕЛЬНО имеют двойную точность по умолчанию?   -  person Amro    schedule 03.06.2012


Ответы (4)


Я не эксперт MatLab, но без кавычек вы передаете результат sqrt(5)/pi в vpa():

  vpa(sqrt(5)/pi)
= vpa(0.7117625434171772)

С кавычками вы передаете выражение sqrt(5)/pi (невычисленное и в точной форме) в vpa(), а затем указываете MatLab вычислить sqrt(5)/pi с переменной точностью.

person Blender    schedule 03.06.2012
comment
Спасибо! В этом есть смысл. Однако почему, когда я ввожу просто sin(pi/4) в vpa, не имеет значения, использую ли я кавычки или нет? - person Kristian; 03.06.2012
comment
Это должно иметь значение. Как указал @Ben Volgt, sin(pi / 4) = sin(45 deg) = sqrt(2) / 2, что иррационально и не будет точно аппроксимироваться поплавками с двойной точностью. - person Blender; 03.06.2012
comment
После прочтения документации кажется, что vpa возвращает результат с двойной точностью , и в этом случае улучшается только точность промежуточных значений. - person Ben Voigt; 03.06.2012
comment
Здорово. Очень признателен! Кажется, я понял это сейчас :) - person Kristian; 03.06.2012

Никогда не предполагайте, что число вроде vpa(sin(pi/4)) является точным с полной точностью, потому что MATLAB обычно вычисляет число внутри вызова vpa, используя арифметику с плавающей запятой, то есть с точностью до 16 цифр.

Однако, похоже, здесь это правильно. Например, мы знаем, что

sin(pi/4) == sqrt(2)/2

Давайте проверим этот результат. Я буду использовать 100-значную точность, сравнивая как vpa, так и мои собственные инструменты HPF.

>> vpa(sin(pi/4),100)
ans =
0.7071067811865475244008443621048490392848359376884740365883398689953662392310535194251937671638207864

>> vpa(sqrt(sym(2))/2,100)
ans =
0.7071067811865475244008443621048490392848359376884740365883398689953662392310535194251937671638207864

>> sqrt(hpf(2,100))/2
ans =
0.7071067811865475244008443621048490392848359376884740365883398689953662392310535194251937671638207864

>> sin(hpf('pi',100)/4)
ans =
0.7071067811865475244008443621048490392848359376884740365883398689953662392310535194251937671638207864

Итак, я предполагаю, что синтаксический анализатор распознал ввод как нечто, что символический набор инструментов может вычислить более точно. Но, как я уже говорил, будьте осторожны. Что такое грех(пи/12)?

>> vpa(sin(pi/12),100)
ans =
0.25881904510252073947640383266843855381011962890625

>> vpa('sin(pi/12)',100)
ans =
0.2588190451025207623488988376240483283490689013199305138140032073150569747488019969223679746942496655

>> vpa(sin(sym('pi')/12),100)
ans =
0.2588190451025207623488988376240483283490689013199305138140032073150569747488019969223679746942496655

>> sin(hpf('pi',100)/12)
ans =
0.2588190451025207623488988376240483283490689013199305138140032073150569747488019969223679746942496655

Видите, что в первом случае парсер нас не спас. В остальных случаях я заставлял MATLAB вычислять правильное значение. Фактически, небольшое усилие даст нам значение sin(pi/12) как sqrt(2)*(sqrt(3) - 1)/4.

>> DefaultNumberOfDigits 100
>> (sqrt(hpf(3)) - 1)*sqrt(hpf(2))/4
ans =
0.2588190451025207623488988376240483283490689013199305138140032073150569747488019969223679746942496655

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

Редактировать: в качестве проверки комментария Амро я с уважением заявляю, что MATLAB делает здесь что-то интересное. Посмотрите, что vpa может вернуть правильные первые 100 цифр числа пи, даже если число пи передано как число с двойной точностью. Поскольку число пи (как двойное) неверно после 16-й десятичной цифры, происходит что-то подозрительное.

>> vpa(pi,100)
ans =
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068

>> vpa('pi',100)
ans =
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068

vpa('pi',100) - vpa(pi,100)
ans =
0.0

В качестве проверки этого факта давайте посмотрим, что находит HPF. HPF фактически принимает значение IEEE 754, хранящееся в двойном значении, а затем преобразует его в число HPF.

>> hpf(pi,100)
ans =
3.141592653589793115997963468544185161590576171875

>> hpf('pi',100)
ans =
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068

>> hpf('pi',100) - hpf(pi,100)
ans =
0.0000000000000001224646799147353177226065932275001058209749445923078164062862089986280348253421170679821480800000000

Итак, ясно, что MATLAB способен распознавать число пи как нечто большее, чем просто значение двойной точности, в котором оно будет передано.

Редактировать2:

На самом деле, немного игры говорит мне, что здесь происходит. VPA сложный, а не синтаксический анализатор. Рассмотрим дробь 7/13. Если мы построим его как двойное, а затем распечатаем значение с плавающей запятой, сохраненное во всей красе, мы увидим, что оно не совсем точное. Это ожидаемо.

>> sprintf('%.100f',7/13)
ans =
0.5384615384615384359179302009579259902238845825195312500000000000000000000000000000000000000000000000

7/13 — повторяющееся десятичное число. Вот правильные цифры:

>> vpa('7/13',100)
ans =
0.5384615384615384615384615384615384615384615384615384615384615384615384615384615384615384615384615385

Теперь предположим, что мы пытаемся создать такое же число. Здесь я передам 7/13 как двойное, но ошибусь в младших десятичных цифрах

>> sprintf('%.100f',0.538461538461538461777777777)
ans =
0.5384615384615384359179302009579259902238845825195312500000000000000000000000000000000000000000000000

Здесь мы видим, что vpa ловит и исправляет «ошибку», которую я сделал, признавая, что то, что я передал, на самом деле точно такое же значение, как и при передаче в 7/13.

>> vpa(0.538461538461538461777777777,100)
ans =
0.5384615384615384615384615384615384615384615384615384615384615384615384615384615384615384615384615385

Конечно, если я передам значение в виде строки, то vpa ошибется.

>> vpa('0.538461538461538461777777777',100)
ans =
0.538461538461538461777777777

Это объясняет, почему vpa может уловить и правильно вычислить vpa(sin(pi/4),100) с требуемой точностью. sin(pi/4) вычисляется как двойное число, но тогда vpa видит его как число, такое же, как версия sqrt(2)/2 с двойной точностью.

Будьте осторожны, конечно. Например, vpa недостаточно умен, чтобы поймать этот простой сдвиг числа пи.

>> vpa(pi + 1,100)
ans =
4.141592653589793115997963468544185161590576171875

>> vpa(pi,100)
ans =
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
person Community    schedule 03.06.2012
comment
просто чтобы устранить путаницу, синтаксический анализатор не обрабатывает входные данные в vpa(sin(pi/4)) как-либо иначе, чем вызов любой другой функции myFcn(sin(pi/4))... MATLAB сначала оценивает выражение с двойной точностью, прежде чем передать результат функции. С конечной точностью результат округляется до ближайшего десятичного значения, которое он может представлять, что VPA рассматривает как точное значение. В этот момент он может вычислить столько десятичных знаков, сколько пожелаете... Я считаю, что @AndrewJanke дал хорошее объяснение в вопрос, на который я ссылался. - person Amro; 04.06.2012
comment
Большое тебе спасибо! Я действительно ценю ваш подробный ответ. - person Kristian; 04.06.2012
comment
@woodchips: кажется, ты тоже прав. Когда вы вызываете vpa с числовым выражением, он внутренне вызывает vpa(sym(expr)). Теперь, когда sym вызывается с числом, он пытается преобразовать число в рациональную форму, чтобы компенсировать ошибку округления (flag = 'r' аргумент), что объясняет, почему VPA исправил ошибку, как вы показали. Если вы хотите получить те же результаты, что и ваш инструмент HPF, я думаю, это будет: vpa(sym('pi')-sym(pi,'d'), 100) - person Amro; 04.06.2012
comment
@woodchips: Неудивительно, что я пропустил эту деталь, просматривая документацию в предыдущие версии, не было очевидно, что такое преобразование имело место, и только в R2012a документ ясно упоминает этот факт... +1 за действительно поучительный ответ - person Amro; 04.06.2012

Если вы получаете точно такой же ответ, вам не нужна арифметика с переменной точностью для начала.

Однако sin(pi/4) должно быть ровно sqrt(2)/2, что иррационально. Вы не должны получать один и тот же ответ с разной точностью. Возможно, вам следует проверить, как вы отображаете (и округляете) результат.

person Ben Voigt    schedule 03.06.2012
comment
Спасибо. Я понимаю это сейчас :). Ценить это! - person Kristian; 03.06.2012

Последний документ по преобразованию числовых в символьные содержит ответ.

sym пытается исправить ошибку округления во входных данных с плавающей запятой, чтобы вернуть точную символьную форму. В частности, sym исправляет ошибку округления в числовых входных данных, которые соответствуют формам p/q, pπ/q, (p/q)^(1/2), 2^q и 10^q, где p и q скромные. -целые числа.

Таким образом, sin(pi/4) является 2^(1/2)/2 или (1/2)^(1/2), поэтому команда vpa распознает его. Однако sqrt(5)/pi не является распознаваемой формой ввода в соответствии с документом.

person Karan Gill    schedule 25.01.2016