Je ne comprends pas pourquoi nous nous soucions des différentes représentations du zéro positif et négatif.
Je me souviens vaguement d'avoir lu des affirmations selon lesquelles une représentation négative de zéro est extrêmement importante dans une programmation impliquant des nombres complexes. Je n'ai jamais eu l'occasion d'écrire du code impliquant des nombres complexes, donc je suis un peu perplexe quant à la raison pour laquelle ce serait le cas.
article de Wikipedia sur le concept n'est pas particulièrement utile; cela ne fait que de vagues affirmations sur le zéro signé rendant certaines opérations mathématiques plus simples en virgule flottante, si je comprends bien. Cette réponse répertorie quelques fonctions qui se comportent différemment, et peut-être que quelque chose pourrait être déduit des exemples si vous savez comment elles pourraient être utilisées. (Bien que l'exemple particulier des racines carrées complexes semble complètement faux , car les deux nombres sont mathématiquement équivalents, sauf si j'ai un malentendu.) Mais Je n'ai pas été en mesure de trouver une déclaration claire du genre de problèmes que vous pourriez rencontrer si ce n'était pas le cas. Les ressources plus mathématiques que j'ai pu trouver indiquent qu'il n'y a pas de distinction entre les deux d'un point de vue mathématique, et l'article de Wikipedia semble suggérer que cela est rarement vu en dehors de l'informatique en dehors de la description des limites.
Alors, pourquoi un zéro négatif est-il précieux en informatique? Je suis sûr que je manque juste quelque chose.
Vous devez garder à l'esprit que dans l'arithmétique FPU, 0 ne doit pas nécessairement signifier exactement zéro, mais aussi une valeur trop petite pour être représentée à l'aide d'un type de données donné, par ex.
a = -1 / 1000000000000000000.0
a est trop petit pour être représenté correctement par float (32 bits), il est donc "arrondi" à -0.
Maintenant, disons que notre calcul continue:
b = 1 / a
Parce que a est float, il en résultera -infinity qui est assez loin de la bonne réponse de -1000000000000000000.0
Maintenant, calculons b s'il n'y a pas -0 (donc a est arrondi à +0):
b = 1 / +0
b = +infinity
Le résultat est de nouveau erroné à cause de l'arrondissement, mais maintenant il est "plus faux" - non seulement numériquement, mais plus important encore à cause du signe différent (le résultat du calcul est + infini, le résultat correct est -1000000000000000000.0).
Vous pourriez toujours dire que cela n'a pas vraiment d'importance car les deux ont tort. L'important est qu'il existe de nombreuses applications numériques où le résultat le plus important du calcul est le signe - par exemple lorsque vous décidez de tourner à gauche ou à droite au carrefour en utilisant un algorithme d'apprentissage automatique, vous pouvez interpréter la valeur positive => tourner à gauche, la valeur négative => tourner à droite, la "magnitude" réelle de la valeur est juste un "coefficient de confiance".
Tout d'abord, comment créez-vous un -0? Il existe deux façons: (1) effectuer une opération à virgule flottante où le résultat mathématique est négatif, mais si proche de zéro qu'il est arrondi à zéro et non à un nombre non nul. Ce calcul donnera un -0. (b) Certaines opérations impliquant des zéros: multipliez un zéro positif par un nombre négatif, ou divisez un zéro positif par un nombre négatif, ou annulez un zéro positif.
Avoir un zéro négatif simplifie un peu la multiplication et la division, le signe de x * y ou x/y est toujours le signe de x, exclusif ou le signe de y. Sans zéro négatif, il faudrait un contrôle supplémentaire pour remplacer -0 par +0.
Il existe des situations très rares où cela est utile. Vous pouvez vérifier si le résultat d'une multiplication ou d'une division est mathématiquement supérieur ou inférieur à zéro, même en cas de sous-dépassement (tant que vous savez que le résultat n'est pas un zéro mathématique). Je ne me souviens pas avoir écrit du code où cela fait une différence.
L'optimisation des compilateurs déteste -0. Par exemple, vous ne pouvez pas remplacer x + 0,0 par x, car le résultat ne doit pas être x si x est -0,0. Vous ne pouvez pas remplacer x * 0.0 par 0.0, car le résultat devrait être -0,0 si x <0 ou x est -0,0.
C # Double conforme à IEEE 754
double a = 3.0;
double b = 0.0;
double c = -0.0;
Console.WriteLine(a / b);
Console.WriteLine(a / c);
impressions:
Infinity
-Infinity
en fait d'expliquer un peu ...
Double d = -0.0;
Cela signifie quelque chose de beaucoup plus proche de d = The Limit of x as x approaches 0-
Ou The Limit of x as x approaches 0 from the negatives
.
Pour répondre au commentaire de Philipp ...
n zéro fondamentalement négatif signifie un dépassement de capacité.
Il y a très peu d'utilisation pratique pour le zéro négatif, le cas échéant ...
par exemple, ce code (encore C #):
double a = -0.0;
double b = 0.0;
Console.WriteLine(a.Equals(b));
Console.WriteLine(a==b);
Console.WriteLine(Math.Sign(a));
donne ce résultat:
True
True
0
Pour expliquer de manière informelle, toutes les valeurs spéciales qu'un virgule flottante IEEE 754 peut avoir (infini positif, infini négatif, NAN, -0,0) n'ont aucune signification au sens pratique. Ils ne peuvent représenter aucune valeur physique, ni aucune valeur qui ait du sens dans le calcul du "monde réel". Ce qu'ils veulent dire, c'est essentiellement ceci:
sqrt(-7)
, ou qu'il n'a pas de limite comme 0/0
Ou comme PositiveInfinity/PositiveInfinity
La question de savoir comment cela se rapporte aux calculs de nombres complexes est vraiment au cœur de la raison pour laquelle +0 et -0 existent en virgule flottante. Si vous étudiez l'analyse complexe, vous découvrez rapidement que les fonctions continues du complexe au complexe ne peuvent généralement pas être traitées comme `` à valeur unique '' à moins que l'on adopte la `` fiction polie '' selon laquelle les sorties forment ce que l'on appelle une `` surface de Riemann ''. Par exemple, le logarithme complexe attribue à chaque entrée un nombre infini de sorties; lorsque vous les "connectez" pour former une sortie continue, vous vous retrouvez avec toutes les pièces réelles formant une surface de "tire-bouchon infini" autour de l'origine. Une courbe continue qui croise l'axe réel "vers le bas du côté imaginaire positif" et une autre courbe qui "s'enroule autour du pôle" et traverse l'axe réel "vers le haut depuis le côté imaginaire négatif" prendra différentes valeurs sur l'axe réel car ils passent sur différentes "feuilles" de la surface de Riemann.
Appliquez maintenant cela à un programme numérique qui calcule à l'aide de virgule flottante complexe. L'action entreprise après un calcul donné peut être très différente selon la "feuille" sur laquelle le programme est actuellement "activé", et le signe du dernier résultat calculé vous indique probablement quelle "feuille". Supposons maintenant que ce résultat soit nul? Rappelez-vous, ici "zéro" signifie vraiment "trop petit pour représenter correctement". Mais si le calcul peut arranger -preserver le signe- (c'est-à-dire se rappeler quelle `` feuille '') lorsque le résultat est nul, alors le code peut vérifier le signe et effectuer la bonne action même dans cette situation.
Bien sûr, il y a beaucoup de hacks qui ont l'air vraiment sympa et ils sont utiles (comme arrondir à -0.0
ou +0.0
mais supposons que nous ayons une représentation de signé int avec un signe moins/plus au début (je sais que cela est résolu par le code binaire U2 en nombres entiers mais supposons une représentation moins complexe de double):
0 111 = 7
^ sign
Et s'il y a un nombre négatif?
1 111 = -7
D'accord, c'est aussi simple que cela. Représentons donc 0:
0 000 = 0
C'est bien aussi. Mais qu'en est-il de 1 000
? Faut-il que ce soit un numéro interdit? Mieux non.
Supposons donc qu'il existe deux types de zéro:
0 000 = +0
1 000 = -0
Eh bien, cela simplifiera nos calculs et donnera fort heureusement quelques fonctionnalités supplémentaires. Alors le +0
et -0
proviennent uniquement de problèmes de représentation binaire.