Формат комплексного номера в Python
Мне интересно, как Python (3.3.0) печатает сложные числа. Я ищу объяснение, а не способ изменить печать.
Пример:
>>> complex(1,1)-complex(1,1)
0j
Почему он просто не печатает "0"? Мое предположение: сохранить вывод сложного типа.
Следующий пример:
>>> complex(0,1)*-1
(-0-1j)
Ну, простой "-1j" или "(-1j)" сделал бы. И почему "-0"?? Разве это не так, как +0? Это не похоже на проблему округления:
>>> (complex(0,1)*-1).real == 0.0
True
И когда мнимая часть становится положительной, -0 исчезает:
>>> complex(0,1)
1j
>>> complex(0,1)*-1
(-0-1j)
>>> complex(0,1)*-1*-1
1j
Еще один пример:
>>> complex(0,1)*complex(0,1)*-1
(1-0j)
>>> complex(0,1)*complex(0,1)*-1*-1
(-1+0j)
>>> (complex(0,1)*complex(0,1)*-1).imag
-0.0
Я что-то пропустил?
Ответы
Ответ 1
Он печатает 0j
, чтобы указать, что он все еще имеет значение complex
. Вы также можете ввести его так:
>>> 0j
0j
Остальное, вероятно, является результатом магии представления IEEE 754 с плавающей запятой, что делает различие между 0 и -0, так называемым подписанный нуль. В принципе, есть один бит, который говорит, является ли число положительным или отрицательным, независимо от того, является ли число нулевым. Это объясняет, почему 1j * -1
дает что-то с отрицательной нулевой вещественной частью: положительный нуль умножается на -1.
-0 требуется стандартом для сравнения равным +0, что объясняет, почему (1j * -1).real == 0.0
все еще сохраняется.
Причина, по которой Python по-прежнему решает распечатать -0, заключается в том, что в сложном мире они имеют значение для разрезов ветвей, например, в phase
function:
>>> phase(complex(-1.0, 0.0))
3.141592653589793
>>> phase(complex(-1.0, -0.0))
-3.141592653589793
Речь идет о мнимой части, а не о реальной части, но легко представить ситуации, в которых знак реальной части будет иметь аналогичную разницу.
Ответ 2
Ответ лежит на исходном коде Python.
Я буду работать с одним из ваших примеров. Пусть
a = complex(0,1)
b = complex(-1, 0)
Когда вы делаете a*b
, вы вызываете эту функцию:
real_part = a.real*b.real - a.imag*b.imag
imag_part = a.real*b.imag + a.imag*b.real
И если вы сделаете это в интерпретаторе python, вы получите
>>> real_part
-0.0
>>> imag_part
-1.0
Из IEEE754 вы получаете отрицательный нуль, а так как что не +0, вы получаете парсеры и реальную часть при ее печати.
if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
/* Real part is +0: just output the imaginary part and do not
include parens. */
...
else {
/* Format imaginary part with sign, real part without. Include
parens in the result. */
...
Я думаю (но я точно не знаю), что обоснование исходит из важности этого знака при вычислении с элементарными сложными функциями (там ссылка на это в статье википедии на подписанный ноль).
Ответ 3
-
0j
представляет собой мнимый литерал, который действительно указывает на комплексное число, а не целое число или число с плавающей запятой.
-
+-0
( "signed zero" ) является результатом соответствия Python IEEE 754 с плавающей запятой, поскольку в Python, complex
по определению представляет пару чисел с плавающей запятой. Из-за последнего нет необходимости печатать или указывать части с нулевой долей для complex
тоже.
-
Часть -0
печатается для точного представления содержимого как repr()
требует документации (repr()
is неявно вызываемый всякий раз, когда результат операции выводится на консоль).
-
Относительно вопроса почему (-0+1j) = 1j
, но (1j*-1) = (-0+1j)
.
Обратите внимание, что (-0+0j)
или (-0.0+0j)
не являются единичными комплексными числами, а выражения - a int
/float
, добавленные к complex
. Чтобы вычислить результат, сначала первое число преобразуется в complex
(-0
→ (0.0,0.0)
, поскольку целые числа не имеют подписанных нулей, -0.0
→ (-0.0,0.0)
). Затем его .real
и .imag
добавляются к соответствующим 1j
, которые являются (+0.0,1.0)
. Результат (+0.0,1.0)
: ^). Чтобы построить комплекс напрямую, используйте complex(-0.0,1)
.
Ответ 4
Что касается первого вопроса: если он просто напечатал 0
, он был бы математически корректным, но вы не знали бы, что имеете дело с объектом complex
vs a int
. Пока вы не укажете .real
, вы всегда получите J-компонент.
Я не уверен, почему вы когда-нибудь получите -0
; это не технически неверно (-1 * 0 = 0)
, но оно синтаксически нечетно.
Что касается остального, странно, что он несовместим, однако ни один из них не является технически правильным, а просто артефактом реализации.