37 votes

Pourquoi (1-x)(1 x) est-il préféré à (1-x 2)?

Je regardais une mise en œuvre de bibliothèque runtime `` dont a été mis en œuvre en calculant:

Cependant, le code qui a calculé effectivement évalué . Y a-t-il une bonne raison de préférer ce dernier ? Je soupçonne que ce dernier réduit l'inexactitude de l'arrondissement pour près de `` zéro, mais je ne peux pas raisonner pourquoi ce serait le cas.

26voto

Eric Postpischil Points 36641

Les dérivés de l' ArcTan(X, Sqrt(1-X*X)) par rapport à X est 1/Sqrt(1-X*X). Cela va à l'infini comme |X| passe à 1. Par conséquent, lorsque X est proche de 1 ou -1, toute erreur dans l'évaluation a un effet énorme sur le résultat. Ainsi, il est essentiel que l'évaluation de minimiser les erreurs dans ces cas.

Lorsque X est proche de 1, l'évaluation de l' 1-X a pas d'erreur (dans la norme IEEE 754 ou tout bon flottante, système de point, parce que l'échelle de la résultat est tel que son bit le moins significatif est au moins aussi bas que le bit le moins significatif dans 1 ou X, donc l'exact résultat mathématique n'a pas de bits à l'extérieur de la disposition significande bits). Depuis 1-X est exact, prendre en compte l'effet de l'erreur en 1+X en considérant la dérivée de ArcTan(X, Sqrt((1-X)*(1+X+e)) à l'égard à e, où e est l'erreur introduite dans l' 1+X de l'opération. Le dérivé est, lorsque X est proche de 1 et e est faible, d'environ -1/10. (Prenant la dérivée avec de l'Érable et de la substitution de 1 pour x rendements en -1/(sqrt(4+2e)*(5+2e). Ensuite, la substitution de 0 pour e les rendements -1/10.) Ainsi, l'erreur en 1+X n'est pas critique.

Par conséquent, l'évaluation de l'expression en tant que ArcTan(X, Sqrt((1-X)*(1+X)) est une bonne manière de l'évaluer.

La situation est symétrique pour X proche de -1. (1+X a pas d'erreur, et 1-X n'est pas critique.)

A l'inverse, si l'on considère l'erreur en X*X, la dérivée de ArcTan(X, Sqrt(1-X*X+e)) à l'égard de e est, lorsque X est proche de 1, environ -1/(2*sqrt(e)*(1+e)), de sorte qu'il est grand lorsque e est petit. Donc une petite erreur dans l'évaluation de la X*X sera la cause d'une grosse erreur dans le résultat, lorsque X est proche de 1.


Demandez Pascal Cuoq, lorsque l'évaluation d'une fonction f(x), nous sommes généralement intéressés à minimiser l'erreur relative dans le résultat final. Et, comme je l'ai souligné, les erreurs qui se produisent lors du calcul sont généralement relative des erreurs dans les résultats en raison de l'arrondi à virgule flottante. J'ai été capable de l'ignorer dans le ci-dessus parce que j'étais considérons la fonction lorsque X est proche de 1, donc, à la fois les valeurs intermédiaires en cours d'examen (1+X et X*X) et la valeur finale avait grandeurs proche de 1, donc en divisant les valeurs de ces grandeurs ne pas changer quoi que ce soit de manière significative.

Toutefois, pour être complet, j'ai examiné la situation de plus près. En d'Érable, j'ai écrit g := arctan(x, sqrt((1-x*x*(1+e0))*(1+e1))*(1+e2)), permettant ainsi d'erreurs relatives e0, e1 et e2 dans les calculs de x*x, 1-x*x, et l' sqrt, respectivement, et j'ai écrit h:= arctan(x, sqrt((1-x)*(1+x)*(1+e0))*(1+e2)) pour l'alternative. Notez que e0, dans ce cas, combine les trois erreurs en 1-x, 1+x, et la multiplication d'entre eux, le terme d'erreur pourrait être l' (1+ea)*(1+eb)*(1+ec), mais c'est bien ce qu' 1+e0 avec un plus grand éventail possible de e0.

Ensuite, j'ai examiné les dérivés de ces fonctions à l'égard de (un à la fois) e0, e1 et e2 divisé par l'abs(f(x)), où f a la fonction idéale, arctan(x, sqrt(1-x*x)). E. g., en d'Érable, j'ai examiné diff(g, e0) / abs(f(x)). Je n'ai pas d'effectuer un plein analytique de l'évaluation de ceux-ci; j'ai examiné les valeurs pour certaines valeurs de x proche de 0 et proche de 1 et pour les valeurs de e0, e1 et e2 dans l'un de leurs limites, -2-54.

Pour x proche de 0, les valeurs étaient tous de grandeur de 1 ou moins. C'est, un parent, une erreur dans le calcul donné lieu à une semblable erreur relative dans le résultat, ou moins.

Pour x proche de 1, les valeurs avec les dérivés de e1 et e2 ont été minuscule, environ 10-8 ou moins. Toutefois, les valeurs avec les dérivés de e0 étaient très différentes pour les deux méthodes. Pour l' 1-x*x méthode, la valeur était d'environ 2•107 (à l'aide de x = 1-2-53). Pour l' (1-x)*(1+x) méthode, la valeur était d'environ 5•10-9.

En résumé, les deux méthodes ne diffèrent pas beaucoup près de x = 0, mais cette dernière méthode est bien plus près de x = 1.

13voto

Mad Physicist Points 3218

Imaginez un cas où est la plus petite valeur positive telle qu'avec l'erreur de roundoff. Dans ce cas, , alors que signifie réellement quelque chose.

11voto

Pascal Cuoq Points 39606

J'ai écrit le programme ci-dessous afin d'obtenir certains résultats empiriques pour la simple précision.

#include <float.h>
#include <math.h>
#include <stdio.h>

long double d1, d2, rel1, rel2;
float i1, i2;
int main() {
  float f;
  for (f = nextafterf(0, 2); f <= 1; f = nextafterf(f, 2))
    {
      long double o = 1.0L - ((long double)f * f);
      float r1 = (1 - f) * (1 + f);
      float r2 = 1 - f * f;
      long double c1 = fabsl(o - r1);
      long double c2 = fabsl(o - r2);
      if (c1 > d1) d1 = c1;
      if (c2 > d2) d2 = c2;
      if (c1 / o > rel1) rel1 = c1 / o, i1 = f;
      if (c2 / o > rel2) rel2 = c2 / o, i2 = f;
    }

  printf("(1-x)(1+x) abs:%Le  relative:%Le\n", d1, rel1);
  printf("1-x*x      abs:%Le  relative:%Le\n\n", d2, rel2);

  printf("input1: %a 1-x:%a 1+x:%a (1-x)(1+x):%a o:%a\n", i1, 1-i1, 1+i1, (1-i1)*(1+i1), (double)(1 - ((long double)i1 * i1)));
  printf("input2: %a x*x:%a 1-x*x:%a o:%a\n", i2, i2*i2, 1 - i2*i2, (double)(1 - ((long double)i2 * i2)));
}

Quelques remarques:

  • J'ai utilisé de 80 bits long double pour calculer les méta-données. Ce n'est pas suffisant pour représenter l'erreur commise pour toutes les valeurs de x, mais je crains le programme qui allait devenir affreusement lent avec plus de précision.
  • La valeur de référence o est calculée en 1.0L - ((long double)f * f). C'est toujours le plus proche, long double nombre au résultat réel, car (long double)f * f est exact (voir, déjà, il semble que la forme 1 - x*x peut parfois être mieux :) ).

J'ai obtenu les résultats ci-dessous:

(1-x)(1+x) abs:8.940394e-08  relative:9.447410e-08
1-x*x      abs:4.470348e-08  relative:8.631498e-05

input1: 0x1.6a046ep-1 1-x:0x1.2bf724p-2 1+x:0x1.b50238p+0 (1-x)(1+x):0x1.0007bep-1 o:0x1.0007bc6a305ep-1
input2: 0x1.ffe96p-1 x*x:0x1.ffd2cp-1 1-x*x:0x1.6ap-12 o:0x1.69f8007p-12

Selon ces résultats, 1 - x*x a une meilleure précision absolue et en (1-x)*(1+x) a beaucoup plus de précision. À virgule flottante est tout au sujet de l'exactitude relative (l'ensemble du système est conçu pour permettre relativement précise de la représentation des petites et des grandes valeurs), de sorte que la dernière forme est préférée.

EDIT: le Calcul de l'erreur finale a plus de sens, comme l'illustre Eric réponse. Une sous-expression dans une expression telle que ArcTan(X, Sqrt(1 - X*X)) pourrait avoir été choisi de ne pas en raison de sa meilleure exactitude dans l'ensemble, mais parce qu'il était exact où il comptait le plus. En ajoutant les lignes ci-dessous pour le corps de la boucle:

  long double a = atan2l(f, sqrtl(o));
  float a1 = atan2f(f, sqrtf(r1));
  float a2 = atan2f(f, sqrtf(r2));
  long double e1 = fabsl(a - a1);
  long double e2 = fabsl(a - a2);
  if (e1 / a > ae1) ae1 = e1 / a, i1 = f;
  if (e2 / a > ae2) ae2 = e2 / a, i2 = f;

Il pourrait faire comme beaucoup de sens d'utiliser atan2l(f, sqrtf(r1)) car je n'ai pas exactement la même fonction ArcTan que votre système. De toute façon, avec ces mises en garde, pour l'expression complète, le maximum de l'erreur relative sur l'intervalle [-1 ... 1] est de 1,4 e-07 pour le (1-x)(1+x) version et 5.5 e-7 pour le 1-x2 version.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X