2012-11-14 10 views
17

Mi chiedo come Python (3.3.0) stampa numeri complessi. Sto cercando una spiegazione, non un modo per cambiare la stampa.Formato del numero complesso in Python

Esempio:

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

Perché non basta stampare "0"? La mia ipotesi è: mantenere l'output di tipo complesso.

Successivo esempio:

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

Ebbene, un semplice "-1j" o "(-1j)" avrebbe fatto. E perché "-0" ?? Non è lo stesso di +0? Non sembra essere un problema di arrotondamento:

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

E quando la parte immaginaria diventa positivo, il -0 svanisce:

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

Ancora un altro esempio:

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

Am Mi manca qualcosa qui?

+0

Per quanto riguarda la parte '0j', è pratica comune in python che' eval (repr (x)) == x' –

risposta

14

Stampa 0j per indicare che è ancora un valore complex. È inoltre possibile digitare di nuovo in questo modo:

>>> 0j 
0j 

Il resto è probabilmente il risultato della magia di IEEE 754 floating point representation, che fa una distinzione tra 0 e -0, il cosiddetto signed zero. Fondamentalmente, c'è un singolo bit che dice se il numero è positivo o negativo, indipendentemente dal fatto che il numero sia pari a zero. Questo spiega perché 1j * -1 dà qualcosa con una parte reale zero negativa: lo zero positivo è stato moltiplicato per -1.

-0 è richiesto dallo standard per confrontare uguale a +0, il che spiega il motivo per cui (1j * -1).real == 0.0 è ancora valido.

La ragione per cui Python decide ancora di stampare il -0, è che nel mondo complesso questi fanno la differenza per i tagli di filiale, ad esempio in the phase function:

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

Questo è circa la parte immaginaria, non la parte reale, ma è facile immaginare situazioni in cui il segno della parte reale farebbe una differenza simile.

+2

Voglio solo aggiungere a questa risposta, che tutti i casi descritti sono validi e pitone opere come previsto. Potete trovare banco di prova speciale con tutte le rappresentazioni relative a IEEE 754: 'lib/test/test_complex.py',' test_negative_zero_repr_str' –

+1

appena provato e sembra siete sulla strada giusta su '-0':' >>> 0j.real .hex() ''0x0.0p + 0'' ' >>> (0j * -1) .real.hex() ' '' -0x0.0p + 0'' –

+0

Confermo il '- 0' parte: 'print (0.0 * -1)' stampa "' -0.0' ". E poiché il tipo 'complex' in Python è una coppia di numeri _float_, non importa se hai specificato la parte frazionaria o meno. –

1

Per quanto riguarda la prima questione: se appena stampato 0 sarebbe matematicamente corretto, ma non si sa che si aveva a che fare con un oggetto complex vs un int. Finché non si specifica .real, si otterrà sempre un componente J.

Non sono sicuro del motivo per cui avresti mai ricevuto -0; non è tecnicamente sbagliato (-1 * 0 = 0) ma è sintatticamente strano.

Per quanto riguarda il resto, è strano che non sia coerente, tuttavia nessuno è tecnicamente corretto, solo un artefatto dell'implementazione.

3

La risposta si trova nel codice sorgente Python stesso.

Lavorerò con uno dei tuoi esempi. Lasciate

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

Quando si esegue a*b si sta chiamando this function:

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

E se lo fai nel l'interprete python, si otterrà

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

Da IEEE754, è 'sta ottenendo un negative zero e dal that's not +0, si ottiene il paren e la parte reale quando si stampa.

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

immagino (ma non so di sicuro) che la logica viene dal l'importanza di quel segno per il calcolo di funzioni complesse elementari (c'è un punto di riferimento per questo alle voci di Wikipedia firmato zero).

2
  • 0j è un imaginary literal che indica infatti un numero complesso piuttosto che un numero intero o in virgola mobile uno.

  • Il +-0 ("zero firmato") è il risultato di conformità di Python per IEEE 754 floating point representation dal momento che in Python, complex is by definition a pair of floating point numbers. A causa di quest'ultimo, non è necessario stampare o specificare parti a frazione zero per un complex.

  • La parte -0 è stampato per rappresentare accuratamente i contenuti as repr()'s documentation demands (repr() viene chiamato implicitamente quando il risultato di un'operazione viene emesso alla console).

  • Riguardo alla domanda perché (-0+1j) = 1j ma (1j*-1) = (-0+1j). noti che (-0+0j) o (-0.0+0j) non sono numeri complessi singoli ma espressioni - un int/float aggiunto a un complex. Per calcolare il risultato, prima il primo numero viene convertito in un complex (-0 ->(-0.0,0.0) ->(0.0,0.0) poiché interi non sono zeri, -0.0 firmato). Poi il suo .real e .imag sono aggiunti a quelli corrispondenti 1j che sono (+0.0,1.0). Il risultato è (+0.0,1.0): ^). Per costruire direttamente un complesso, utilizzare complex(-0.0,1).

+0

Grazie per la spiegazione su str/repr. Ma qui una "stampa (0j * -1)" restituisce ancora "(-0 + 0j)", quindi si comporta allo stesso modo. Inoltre, * se * python si attacca così strettamente allo standard IEEE con repr(), perché "print (-0 + 0j)" restituisce "0j"? – cxxl

+0

2cxxl: '(-0 + 0j)' non è un singolo numero complesso ma un'espressione - un numero intero aggiunto a un complesso. Quando il risultato è calcolato, '-0' viene convertito in un complesso e aggiunto al suo' .real' che è '0'. Il risultato è '+ 0': ^) –

+0

Dato che la mia ipotesi su' str() 'si è dimostrata falsa in questo caso, l'ho cancellato. –