В MATLAB переменные ДЕЙСТВИТЕЛЬНО имеют двойную точность по умолчанию?

52

Этот вопрос возник из чего-то странного, что я заметил после изучения этого вопроса далее...

Я всегда понимал переменные MATLAB как double-precision по умолчанию. Итак, если бы я должен был сделать что-то вроде объявления переменной с 20 цифрами после десятичной точки:

>> num = 2.71828182845904553488;
>> class(num)  %# Display the variable type
ans =
double

Я ожидал бы, что последние 4 цифры будут проигнорированы, так как относительная точность с плавающей точкой составляет порядка 10 -16

>> eps(num)
ans =
    4.440892098500626e-016

Если я попытаюсь отобразить число с более чем 16 цифрами после десятичной точки (используя FPRINTF или SPRINTF), я получаю то, что ожидаю увидеть:

>> fprintf('%0.20f\n',num)
2.71828182845904550000
>> sprintf('%0.20f',num)
ans =
2.71828182845904550000

Другими словами, цифры с 17 по 20 - все 0.

Но все странно, когда я передаю num в арифметическую функцию с переменной точностью в Symbolic Toolbox, сообщая ему, чтобы он представлял число, используя 21 цифру точности:

>> vpa(num,21)
ans =
2.71828182845904553488

ЧТО?! Последние четыре цифры снова появились! Разве они не были потеряны, когда исходный номер, который я ввел, был сохранен как переменная с двойной точностью num? Поскольку num является переменной с двойной точностью, когда она передается в vpa, как vpa знал, какими они были?

Мое лучшее предположение о том, что происходит, состоит в том, что MATLAB внутренне представляет num с большей точностью, чем double, поскольку я инициализировал его числом с большим количеством цифр за десятичной точкой, чем может обрабатывать переменная с двойной точностью. Это действительно то, что происходит, или что-то еще происходит?



БОНУС: И вот дополнительный источник путаницы, если у вас еще нет мигрени из вышеперечисленного...

>> num = 2.71828182845904553488;  %# Declare with 20 digits past the decimal
>> num = 2.718281828459045531;    %# Re-declare with 18 digits past the decimal
>> vpa(num,21)
ans =
2.71828182845904553488  %# It the original 20-digit number!!!
Теги:
floating-point
variables
precision

1 ответ

53
Лучший ответ

Они - двойники. Vpa() просто выбирает отображение незначительных цифр за пределами относительной точности с плавающей точкой, где printf() и disp() усекаются или обнуляются.

Вы получаете только свои исходные четыре цифры, потому что буквальный, который вы выбрали для инициализации num, просто является точным десятичным расширением двоичного двойного значения, поскольку он был скопирован и вставлен с вывода расширения фактическое двойное значение от другого вопроса. Он не будет работать для других близлежащих значений, как показано в добавлении к вашему "БОНУСУ".

Точнее, все числовые литералы в Matlab производят значения типа double. Они преобразуются в двоичное двойное значение, которое ближе всего к десятичному значению, которое они представляют. По сути, цифры в литеральном представлении за пределами точности двойного типа молча отбрасываются. Когда вы копируете и вставляете вывод vpa для создания новой переменной, как это сделал другой вопрос с инструкцией "e =...", вы инициализируете значение из литерала вместо прямого обращения с результатом предыдущее выражение.

Различия здесь только в форматировании вывода. Я думаю, что происходит то, что vpa() берет двойной двойной двоичный двойник и рассматривает его как точное значение. Для данного значения двоичной мантиссы-экспоненты вы можете рассчитать десятичный эквивалент произвольно большого количества десятичных знаков. Если у вас ограниченная точность ( "ширина" ) в двоичном значении, как и у любого типа данных фиксированного размера, значительная часть этих десятичных цифр значительна. Параметры Sprintf() и Matlab по умолчанию обрабатывают это путем усечения вывода или отображения незначительных цифр как 0. Vpa() игнорирует пределы точности и продолжает вычислять столько знаков после запятой, сколько вы запрашиваете.

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

Вот способ показать это. Эти значения x одинаковы при сохранении в двойниках, и все они будут представлены одинаковыми с помощью vpa().

x = [
    2.7182818284590455348848081484902650117874145507812500
    2.7182818284590455348848081484902650117874145507819999
    2.7182818284590455348848
    2.71828182845904553488485555555555555555555555555555
    exp(1)
    ]
unique(x)

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

x0 = exp(1)
x1 = x0 + eps(x0)

Vpa (x0) и vpa (x1) должны давать выходы, которые сильно отличаются от 16-й цифры. Однако вы не должны создавать двойное значение x, так что vpa (x) создает десятичное представление, которое находится между vpa (x0) и vpa (x1).

(ОБНОВЛЕНИЕ: Амро указывает, что вы можете использовать fprintf('%bx\n', x) для отображения точного представления базового двоичного значения в шестнадцатеричном формате. Вы можете использовать это, чтобы подтвердить, что карта букв в том же двойном.)

Я подозреваю, что vpa() ведет себя так, потому что обрабатывает свои входы как точные значения и полиморфно поддерживает другие типы Matlab из Symbolic Toolbox, которые имеют более высокую точность, чем двойные. Эти значения должны быть инициализированы с помощью других средств, кроме числовых литералов, поэтому sym() принимает строку как входную и "vpa (exp (1))" отличается от "vpa (sym ('exp (1)" ) )".

Имеют смысл? Извините за долговечность.

(Примечание. У меня нет Symbolic Toolbox, поэтому я не могу сам тестировать vpa().)

  • 1
    +1 за поучительный ответ на интересный вопрос!
  • 1
    Ага! Так что я просто использовал точное десятичное расширение двоичного значения в качестве моего номера теста. Все это имеет смысл сейчас! Не уверен, как мне удалось это пропустить, хотя ... возможно, это было из-за недостатка сна, так как моя дочь прорезывает зубы и поддерживает меня всю ночь. ;)
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню