В MATLAB переменные ДЕЙСТВИТЕЛЬНО имеют двойную точность по умолчанию? - PullRequest
69 голосов
/ 19 ноября 2010

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

Я всегда понимал переменные 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

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

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



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

>> 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's the original 20-digit number!!!

Ответы [ 2 ]

62 голосов
/ 19 ноября 2010

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

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

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

Различия здесь только в форматировании вывода. Я думаю, что происходит то, что vpa() берет двоичный двойной тип двойной точности и рассматривает его как точное значение. Для заданного двоичного значения показателя мантиссы вы можете вычислить десятичный эквивалент для произвольного числа десятичных разрядов. Если у вас есть ограниченная точность («ширина») в двоичном значении, как вы делаете с любым типом данных фиксированного размера, значительным является только очень много из этих десятичных цифр. printf() и отображение по умолчанию в 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() сам.)

3 голосов
/ 23 августа 2018

first:

похоже, что sprintf и fprintf ведут себя по-разному в разных версиях MATLAB, например, в MATLAB 2018 a

num=2.7182818284590666666666;    
sprintf('%0.70f', num)
ans =
'2.7182818284590668511668809514958411455154418945312500000000000000000000'

second:

Числа с плавающей точкой

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

Двойная точность с плавающей точкой

MATLAB создает тип данных двойной точности (или двойной) в соответствии спо стандарту IEEE® 754 для двойной точности.Любое значение, хранящееся как двойное число, требует 64 бита, отформатированного, как показано в таблице ниже:

Биты: 63
Использование: Знак (0 = положительный, 1 = отрицательный)

Биты:62 до 52 Использование: экспонента, смещение 1023

Биты: от 51 до 0 Использование: Фракция f числа 1.f

см. Эту ссылку для получения дополнительной информации

Между 252 = 4,503,599,627,370,496 и 253 = 9,007,199,254,740,992 представимые числа являются в точности целыми числами.Для следующего диапазона, от 253 до 254, все умножается на 2, поэтому представимые числа являются четными и т. Д. И наоборот, для предыдущего диапазона от 2 ^ 51 до 2 ^ 52 интервал равен 0,5 и т. Д.

Интервал как часть чисел в диапазоне от 2 ^ n до 2 ^ n + 1 составляет 2 ^ n − 52.Таким образом, максимальная относительная ошибка округления при округлении числа до ближайшего представимого числа (эпсилон машины) составляет 2 ^ −53.

, поэтому в вашем случае, когда n = 1 (2 ^ 1 <= num<= 2 ^ 2) интервал равен 2 ^ -51, </p>

Я думаю, можно с уверенностью предположить, что алгоритмы sprintf и sprintf для отображения чисел хитры, а тип MATLAB Double основан на стандарте IEEE,


о VPA:

vpa Использует защитные цифры для поддержания точности

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

Численно приблизительно 1/3 с использованием четырех значащих цифр.

a = vpa(1/3, 4)
a =
0.3333

Приблизительный результатс использованием 20 цифр.Результат показывает, что при наборе a инструментарий внутренне использовал более четырех цифр.Последние цифры в результате неверны из-за ошибки округления.

vpa(a, 20)
ans =
0.33333333333303016843

проблема, с которой вы можете столкнуться из-за пробелов, алгоритма цифр gaurd и проблемы округления,

например, используя matlab 2018 a:

 sprintf('%0.28f', 8.0)
 ans =
 '8.0000000000000000000000000000'

, но:

sprintf('%0.28f', 8.1)
ans =
'8.0999999999999996447286321199'

, поскольку число находится в диапазоне от 2 ^ 3 до 2 ^ 4, поэтому интервал составляет 2 ^ -49 (= 1,77 е-15), поэтому число действует до 15-го знака после запятой и

sprintf('%0.28f', 64.1)
ans =
'64.0999999999999943156581139192'

, поскольку число находится в диапазоне от 2 ^ 6 до 2 ^ 7, поэтому расстояние составляет 2 ^ -46 (= 1,42 е14) поэтому номер действителен до 14 знака после запятой

...