Точность числа комплексных чисел - PullRequest
0 голосов
/ 25 мая 2018

Рассмотрим следующий код Python, который выполняет некоторые простые арифметические операции над комплексными числами в Python:

import numpy as np

s = 2
l = 5
v = np.array([np.exp(1j*2*np.pi/l)])
A = pow(s*v, l) + s*v

#Print the precision of np.complex128
print np.finfo(np.complex128).precision

#Export using 20 decimal places for real and imaginary parts
np.savetxt('A.csv', A, fmt=['%.20e%+.20ej'], delimiter=',')

Я видел этот ответ , чтобы вывести точность np.complex128переменная в Python, и я получаю 15. Когда я проверяю значение A в Spyder, я вижу (32.61803398874989+1.9021130325903035j), чья мнимая часть имеет более 15 десятичных знаков.Кроме того, экспортированный CSV-файл имеет значение 3.26180339887498931262e+01+1.90211303259030350965e+00j, чьи действительные и мнимые части имеют 20 полезных десятичных знаков.

Я запутался здесь: если точность равна 15, то что этолишние десятичные знаки?Согласно здесь , np.complex128 состоит из двух 64-разрядных чисел с плавающей запятой, одного для действительного и одного для мнимой части.

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

Я использую Python 2.7.14 и Numpy 1.14.3.

1 Ответ

0 голосов
/ 26 мая 2018

Полный ответ занял бы много места.Для начала вам нужно прочитать книгу о численном анализе, которая объясняет, как двоичные типы с плавающей запятой работают и хранятся.Но вот некоторые частичные объяснения.

64-битные значения с плавающей запятой хранятся в двоичном, а не в десятичном виде.Поэтому большая часть того, что говорится о десятичных цифрах, должна быть приблизительной, а не полной историей.

Когда np.finfo(np.complex128).precision (или что-то еще) говорит, что существует 15 значащих десятичных цифр, это означает, что вы не можете рассчитывать набольше, чем те 15 цифр.Вот пример, взятый из моей консоли IPython.

In [4]: 9.000000000000001
Out[4]: 9.000000000000002

In [5]: 9.000000000000001 == 9.000000000000002
Out[5]: True

Python, который по умолчанию использует 64-битный тип с плавающей запятой для любого числа с десятичной запятой, обрабатывает эти два 16-десятичных числакак идентичные.При использовании числа 9.000000000000001 только первые 15 десятичных цифр гарантированно сохраняются правильно.Все, что после этого не гарантируется, и вы видите, что в этом случае 1 в основном изменяется на 2.

Иногда вы можете получить больше, чем эти 15 десятичных цифр.Например, поскольку числа хранятся в двоичном формате, число, немного превышающее 1.0, будет иметь больше двоичных цифр после radix point , чем число, подобное 9.0.Это связано с тем, что 9 использует 4 двоичные цифры, а 1 использует только одну, поэтому после точки радиуса можно использовать еще 3 двоичные цифры.Итак, давайте посмотрим на мнимую часть вашего числа, 1.9021130325903035:

In [17]: 1.902113032590303 == 1.9021130325903035
Out[17]: False

In [18]: 1.9021130325903036 == 1.9021130325903035
Out[18]: True

In [19]: 1.902113032590304 == 1.9021130325903035
Out[19]: False

Мы видим, что, хотя число показывает 17 десятичных цифр, когда мы меняем последнюю цифру с 5 на 6, Python не видит изменений,Но есть изменение, если мы округлим это число до 16 десятичных цифр, вверх или вниз.Таким образом, мы можем сказать, что число хранится до 16 десятичных цифр и немного больше.Вот почему, когда я объясняю точность, я говорю, что число с плавающей запятой гарантированно будет иметь 15 значащих десятичных цифр, но может иметь 16-ю, а фактическая точность немного больше этой.Вкратце, существует 15 или 16 значащих десятичных цифр.

В Python есть два основных способа печати числа с плавающей запятой: функция str и функция repr.Функция str просто печатает, так что пользователь может понять результат, не вдаваясь в подробности.Функция repr дает больше деталей и пытается напечатать настолько много деталей, что сохраненные данные могут быть полностью определены тем, что напечатано.Обратите внимание, что repr используется незаметно в некоторых случаях, например, при вводе числового значения в консоли или, как вы видели, при помощи проводника переменных в Spyder.

Когда Spyder или Python делает repr наВаш номер 1.9021130325903035 дает достаточно цифр, чтобы полностью определить номер.Как мы видели выше, если бы он показывал только 16 десятичных цифр, отбрасывая окончательную 5, результат был бы немного отличным от того, что сохранено.Поэтому Python печатает дополнительную цифру, чтобы вы могли знать, каково значение.Если бы Python напечатал окончательный 6, а не 5, значение было бы таким же, но если бы Python пропустил эту цифру полностью, значение было бы изменено.Таким образом, Python через repr ошибается, давая слишком много цифр.Несмотря на то, что напечатаны 17 цифр, достоверны только 16 из них.

Наконец, ваш CSV-файл показывает 20 десятичных цифр, потому что вы сказали Python отображать 20 десятичных цифр из-за спецификаторов %.20e в np.savetxtкоманда.Эти 20 десятичных цифр не являются всеми " полезными десятичными знаками", несмотря на то, что вы написали.Только первые 16 или 17 полезны в мнимой части, в зависимости от того, как вы определяете «полезный».Python в основном добавил двоичные биты, все нули, к сохраненному значению и вывел его в 20 десятичных цифр.Но эти нулевые двоичные биты не были сохранены в значении.

...