>>> (float('inf')+0j)*1
(inf+nanj)
Pourquoi? Cela a causé un vilain bogue dans mon code.
Pourquoi 1
l'identité multiplicative ne donne- (inf + 0j)
t-elle pas?
>>> (float('inf')+0j)*1
(inf+nanj)
Pourquoi? Cela a causé un vilain bogue dans mon code.
Pourquoi 1
l'identité multiplicative ne donne- (inf + 0j)
t-elle pas?
Réponses:
Le 1
est d'abord converti en un nombre complexe 1 + 0j
, ce qui conduit ensuite à une inf * 0
multiplication, ce qui donne un nan
.
(inf + 0j) * 1
(inf + 0j) * (1 + 0j)
inf * 1 + inf * 0j + 0j * 1 + 0j * 0j
# ^ this is where it comes from
inf + nan j + 0j - 0
inf + nan j
1
est jeté 1 + 0j
.
array([inf+0j])*1
évalue également à array([inf+nanj])
. En supposant que la multiplication réelle se produit quelque part dans le code C / C ++, cela signifierait-il qu'ils ont écrit du code personnalisé pour émuler le comportement CPython, plutôt que d'utiliser _Complex ou std :: complex?
numpy
a une classe centrale ufunc
dont dérivent presque tous les opérateurs et fonctions. ufunc
s'occupe de la diffusion de la gestion des enjambées tout cet administrateur délicat qui rend le travail avec des tableaux si pratique. Plus précisément, la répartition du travail entre un opérateur spécifique et la machine générale est que l'opérateur spécifique implémente un ensemble de "boucles les plus internes" pour chaque combinaison de types d'éléments d'entrée et de sortie qu'il souhaite gérer. La machinerie générale s'occupe de toutes les boucles extérieures et sélectionne la boucle la plus intérieure qui correspond le mieux ...
types
attribut pour np.multiply
ce rendement, ['??->?', 'bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L', 'qq->q', 'QQ->Q', 'ee->e', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D', 'GG->G', 'mq->m', 'qm->m', 'md->m', 'dm->m', 'OO->O']
nous pouvons voir qu'il n'y a presque pas de types mixtes, en particulier, aucun qui mélange float "efdg"
avec complex "FDG"
.
D'un point de vue mécanique, la réponse acceptée est, bien sûr, correcte, mais je dirais qu'une réponse plus profonde peut être donnée.
Tout d'abord, il est utile de clarifier la question comme le fait @PeterCordes dans un commentaire: "Y a-t-il une identité multiplicative pour les nombres complexes qui fonctionne sur inf + 0j?" ou en d'autres termes, est-ce que OP voit une faiblesse dans la mise en œuvre informatique de la multiplication complexe ou y a-t-il quelque chose de conceptuellement incohérent avecinf+0j
En utilisant les coordonnées polaires, nous pouvons voir la multiplication complexe comme une mise à l'échelle et une rotation. En tournant un "bras" infini même de 0 degré comme dans le cas de la multiplication par un, nous ne pouvons pas nous attendre à placer sa pointe avec une précision finie. Donc, en effet, il y a quelque chose de fondamentalement faux avec inf+0j
, à savoir que dès que nous sommes à l'infini, un décalage fini devient dénué de sens.
Contexte: La "grande chose" autour de laquelle cette question tourne est la question d'étendre un système de nombres (pensez aux nombres réels ou complexes). Une des raisons pour lesquelles on pourrait vouloir faire cela est d'ajouter un concept d'infini, ou de "compacter" si l'on se trouve être un mathématicien. Il y a aussi d'autres raisons ( https://en.wikipedia.org/wiki/Galois_theory , https://en.wikipedia.org/wiki/Non-standard_analysis ), mais celles-ci ne nous intéressent pas ici.
La difficulté à propos d'une telle extension est, bien sûr, que nous voulons que ces nouveaux nombres s'inscrivent dans l'arithmétique existante. Le moyen le plus simple est d'ajouter un seul élément à l'infini ( https://en.wikipedia.org/wiki/Alexandroff_extension ) et de le rendre égal à tout sauf à zéro divisé par zéro. Cela fonctionne pour les réels ( https://en.wikipedia.org/wiki/Projectively_extended_real_line ) et les nombres complexes ( https://en.wikipedia.org/wiki/Riemann_sphere ).
Alors que la compactification en un point est simple et mathématiquement valable, des extensions "plus riches" comprenant de multiples infinties ont été recherchées. La norme IEEE 754 pour les nombres à virgule flottante réels a + inf et -inf ( https://en.wikipedia.org/wiki/Extended_real_number_line ). Cela semble naturel et simple, mais nous oblige déjà à sauter à travers des cerceaux et à inventer des trucs comme-0
https://en.wikipedia.org/wiki/Signed_zero
Qu'en est-il des extensions plus d'un inf du plan complexe?
Dans les ordinateurs, les nombres complexes sont généralement implémentés en collant deux réels fp ensemble, un pour le réel et un pour la partie imaginaire. C'est parfaitement bien tant que tout est fini. Mais dès que l'on considère l'infini, les choses deviennent délicates.
Le plan complexe a une symétrie de rotation naturelle, qui s'accorde bien avec l'arithmétique complexe, car multiplier le plan entier par e ^ phij équivaut à une rotation phi radian autour 0
.
Maintenant, pour garder les choses simples, le complexe fp utilise simplement les extensions (+/- inf, nan, etc.) de l'implémentation de nombres réels sous-jacente. Ce choix peut sembler si naturel qu'il n'est même pas perçu comme un choix, mais regardons de plus près ce que cela implique. Une simple visualisation de cette extension du plan complexe ressemble à (I = infini, f = fini, 0 = 0)
I IIIIIIIII I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I ffff0ffff I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I IIIIIIIII I
Mais comme un vrai plan complexe est celui qui respecte la multiplication complexe, une projection plus informative serait
III
I I
fffff
fffffff
fffffffff
I fffffffff I
I ffff0ffff I
I fffffffff I
fffffffff
fffffff
fffff
I I
III
Dans cette projection, nous voyons la "distribution inégale" des infinis qui est non seulement laide mais aussi la racine des problèmes du genre OP a souffert: la plupart des infinis (ceux des formes (+/- inf, finies) et (finies, + / -inf) sont regroupés dans les quatre directions principales toutes les autres directions sont représentées par seulement quatre infinis (+/- inf, + -inf). Il n'est pas surprenant que l'extension de la multiplication complexe à cette géométrie soit un cauchemar .
L'annexe G de la spécification C99 fait de son mieux pour le faire fonctionner, y compris en contournant les règles sur la façon dont inf
et nan
interagir (essentiellement des inf
atouts nan
). Le problème d'OP est contourné en ne faisant pas la promotion de réels et d'un type purement imaginaire proposé en complexe, mais le fait que le réel 1 se comporte différemment du complexe 1 ne me semble pas être une solution. Fait révélateur, l'annexe G ne précise pas complètement ce que devrait être le produit de deux infinis.
Il est tentant d'essayer de résoudre ces problèmes en choisissant une meilleure géométrie d'infinis. Par analogie avec la ligne réelle étendue, nous pourrions ajouter une infinité pour chaque direction. Cette construction est similaire au plan projectif mais ne regroupe pas les directions opposées. Les infinis seraient représentés en coordonnées polaires inf xe ^ {2 omega pi i}, la définition des produits serait simple. En particulier, le problème d'OP serait résolu tout naturellement.
Mais c'est là que s'arrête la bonne nouvelle. D'une certaine manière, nous pouvons être renvoyés à la case départ en exigeant - pas sans raison - que nos infinis de style nouveau prennent en charge des fonctions qui extraient leurs parties réelles ou imaginaires. L'addition est un autre problème; en ajoutant deux infinis non antipodaux, nous devrons définir l'angle sur indéfini, c'est-à-dire nan
(on pourrait dire que l'angle doit se trouver entre les deux angles d'entrée mais il n'y a pas de moyen simple de représenter cette "nanitude partielle")
Au vu de tout cela, peut-être que la bonne vieille compactification en un point est la chose la plus sûre à faire. Peut-être que les auteurs de l'annexe G ont ressenti la même chose lorsqu'ils ont mandaté une fonction cproj
qui regroupe tous les infinis ensemble.
Voici une question connexe à laquelle ont répondu des personnes plus compétentes que moi sur le sujet.
nan != nan
. Je comprends que cette réponse est à moitié plaisante, mais je ne vois pas pourquoi elle devrait être utile au PO tel qu'il est écrit.
==
(et étant donné qu'ils ont accepté l'autre réponse), il semble que c'était juste un problème de la façon dont le PO a exprimé le titre. J'ai reformulé le titre pour corriger cette incohérence. (Invalider intentionnellement la première moitié de cette réponse parce que je suis d'accord avec @cmaster: ce n'est pas le sujet de cette question).
Ceci est un détail d'implémentation de la façon dont la multiplication complexe est implémentée dans CPython. Contrairement à d'autres langages (par exemple C ou C ++), CPython adopte une approche quelque peu simpliste:
Py_complex
_Py_c_prod(Py_complex a, Py_complex b)
{
Py_complex r;
r.real = a.real*b.real - a.imag*b.imag;
r.imag = a.real*b.imag + a.imag*b.real;
return r;
}
Un cas problématique avec le code ci-dessus serait:
(0.0+1.0*j)*(inf+inf*j) = (0.0*inf-1*inf)+(0.0*inf+1.0*inf)j
= nan + nan*j
Cependant, on aimerait avoir -inf + inf*j
comme résultat.
À cet égard, les autres langages ne sont pas loin: la multiplication de nombres complexes n'a pas été pendant longtemps une partie de la norme C, incluse uniquement dans C99 en annexe G, qui décrit comment une multiplication complexe doit être effectuée - et ce n'est pas aussi simple que la formule école ci-dessus! Le standard C ++ ne spécifie pas comment la multiplication complexe devrait fonctionner, donc la plupart des implémentations du compilateur reviennent à l'implémentation C, qui peut être conforme à C99 (gcc, clang) ou non (MSVC).
Pour l'exemple "problématique" ci-dessus, les implémentations conformes à C99 (qui sont plus compliquées que la formule scolaire) donneraient ( voir en direct ) le résultat attendu:
(0.0+1.0*j)*(inf+inf*j) = -inf + inf*j
Même avec la norme C99, un résultat sans ambiguïté n'est pas défini pour toutes les entrées et il peut être différent même pour les versions compatibles C99.
Un autre effet secondaire de float
ne pas être promu complex
en C99 est que la multiplication inf+0.0j
avec 1.0
ou 1.0+0.0j
peut conduire à des résultats différents (voir ici en direct):
(inf+0.0j)*1.0 = inf+0.0j
(inf+0.0j)*(1.0+0.0j) = inf-nanj
, la partie imaginaire étant -nan
et non nan
(comme pour CPython) ne joue pas de rôle ici, car tous les nans tranquilles sont équivalents (voir ceci ), même certains d'entre eux ont un bit de signe défini (et donc imprimé comme "-", voir ceci ) et d'autres non.Ce qui est au moins contre-intuitif.
Ce que j'en retire, c'est qu'il n'y a rien de simple dans la multiplication (ou division) de nombres complexes "simples" et lors du passage d'un langage à l'autre ou même d'un compilateur, il faut se préparer à des bogues / différences subtiles.
printf
et similaire avec double: ils regardent le bit de signe pour décider si "-" doit être imprimé ou non (peu importe si c'est nan ou non). Donc vous avez raison, il n'y a pas de différence significative entre "nan" et "-nan", corrigeant bientôt cette partie de la réponse.
Drôle de définition de Python. Si nous réglons cela avec un stylo et du papier , je dirais que ce résultat attendu serait expected: (inf + 0j)
comme vous l' avez dit , parce que nous savons que nous entendons la norme de 1
façon(float('inf')+0j)*1 =should= ('inf'+0j)
:
Mais ce n'est pas le cas comme vous pouvez le voir ... lorsque nous l'exécutons, nous obtenons:
>>> Complex( float('inf') , 0j ) * 1
result: (inf + nanj)
Python comprend cela *1
comme un nombre complexe et non comme la norme de 1
donc il interprète comme *(1+0j)
et l'erreur apparaît lorsque nous essayons de faire inf * 0j = nanj
comme ne inf*0
peut pas être résolu.
Ce que vous voulez réellement faire (en supposant que 1 est la norme de 1):
Rappelons que si z = x + iy
est un nombre complexe avec une partie réelle x et une partie imaginaire y, le conjugué complexe de z
est défini comme z* = x − iy
, et la valeur absolue, également appelée le, norm of z
est définie comme:
En supposant que 1
la norme 1
est de faire quelque chose comme:
>>> c_num = complex(float('inf'),0)
>>> value = 1
>>> realPart=(c_num.real)*value
>>> imagPart=(c_num.imag)*value
>>> complex(realPart,imagPart)
result: (inf+0j)
pas très intuitif je sais ... mais parfois les langages de codage sont définis d'une manière différente de ce que nous utilisons dans notre vie quotidienne.