Formato de número complejo en Python

Me pregunto sobre la forma en que Python (3.3.0) imprime números complejos. Estoy buscando una explicación, no una forma de cambiar la impresión.

Ejemplo:

>>> complex(1,1)-complex(1,1) 0j 

¿Por qué no solo imprime “0”? Mi conjetura es: para mantener la salida de tipo complejo.

Siguiente ejemplo:

 >>> complex(0,1)*-1 (-0-1j) 

Bueno, un simple “-1j” o “(-1j)” habría hecho. ¿Y por qué “-0”? ¿No es lo mismo que +0? No parece ser un problema de redondeo:

     >>> (complex(0,1)*-1).real == 0.0 True 

    Y cuando la parte imaginaria se vuelve positiva, el -0 se desvanece:

     >>> complex(0,1) 1j >>> complex(0,1)*-1 (-0-1j) >>> complex(0,1)*-1*-1 1j 

    Otro ejemplo más:

     >>> 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 

    ¿Me estoy perdiendo de algo?

    Imprime 0j para indicar que sigue siendo un valor complex . También puedes escribirlo de esa manera:

     >>> 0j 0j 

    El rest es probablemente el resultado de la magia de la representación de punto flotante IEEE 754 , que hace una distinción entre 0 y -0, el llamado cero firmado . Básicamente, hay un solo bit que dice si el número es positivo o negativo, independientemente de que el número sea cero. Esto explica por qué 1j * -1 da algo con una parte real cero negativa: el cero positivo se multiplicó por -1.

    El estándar exige que -0 se compare igual a +0, lo que explica por qué (1j * -1).real == 0.0 aún se mantiene.

    La razón por la que Python aún decide imprimir el -0, es que en el mundo complejo estos hacen una diferencia para los cortes de twig, por ejemplo en la función de phase :

     >>> phase(complex(-1.0, 0.0)) 3.141592653589793 >>> phase(complex(-1.0, -0.0)) -3.141592653589793 

    Se trata de la parte imaginaria, no de la parte real, pero es fácil imaginar situaciones en las que el signo de la parte real haría una diferencia similar.

    La respuesta está en el código fuente de Python.

    Trabajaré con uno de tus ejemplos. Dejar

     a = complex(0,1) b = complex(-1, 0) 

    Cuando haces a*b estás llamando a esta función :

     real_part = a.real*b.real - a.imag*b.imag imag_part = a.real*b.imag + a.imag*b.real 

    Y si haces eso en el intérprete de python, obtendrás

     >>> real_part -0.0 >>> imag_part -1.0 

    Desde IEEE754, obtienes un cero negativo , y como eso no es +0 , obtienes el parens y la parte real al imprimirlo.

     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. */ ... 

    Supongo (pero no estoy seguro) que la razón viene de la importancia de ese signo al calcular con funciones complejas elementales (hay una referencia para esto en el artículo de wikipedia sobre el cero firmado).

    • 0j es un literal imaginario que de hecho indica un número complejo en lugar de uno entero o de coma flotante.

    • El +-0 (“cero con signo”) es el resultado de la conformidad de Python con la representación de punto flotante IEEE 754, ya que en Python, el complex es, por definición, un par de números de punto flotante . Debido a esto último, no es necesario imprimir ni especificar partes de fracción cero para un complex también.

    • La parte -0 se imprime para representar con precisión los contenidos como las demandas de documentación de repr() ( repr() se llama implícitamente cada vez que el resultado de una operación se envía a la consola).

    • Con respecto a la pregunta de por qué (-0+1j) = 1j pero (1j*-1) = (-0+1j) . Tenga en cuenta que (-0+0j) o (-0.0+0j) no son números complejos simples sino expresiones , un int / float agregado a un complex . Para calcular el resultado, primero el primer número se convierte en un complex ( -0 -> (0.0,0.0) ya que los enteros no tienen ceros firmados, -0.0 -> (-0.0,0.0) ). Luego, su .real y .imag se agregan a los correspondientes de 1j que son (+0.0,1.0) . El resultado es (+0.0,1.0) : ^). Para construir un complejo directamente, use complex(-0.0,1) .

    En lo que respecta a la primera pregunta: si solo se imprimiera 0 sería matemáticamente correcto, pero no sabría que se trata de un objeto complex frente a un int . Mientras no especifique .real , siempre obtendrá un componente J.

    No estoy seguro de por qué alguna vez obtendrías -0 ; no es técnicamente incorrecto (-1 * 0 = 0) pero es sintácticamente extraño.

    En cuanto al rest, es extraño que no sea consistente, sin embargo, ninguno es técnicamente correcto, solo un artefacto de la implementación.