Vous avez remis en question la réponse en disant qu'il faut utiliser une plus grande précision pendant la sommation, mais je ne vois pas pourquoi. Cette réponse est correcte. Considérez cette version simplifiée avec des chiffres complètement confectionnés:
#include <iostream>
#include <iomanip>
float w = 0.;
float calcFloat(const int* origin, int n)
{
float d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
float calcDouble(const int* origin, int n)
{
double d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
int main()
{
int o[] = { 1111, 22222, 33333, 444444, 5555 };
std::cout << std::setprecision(9) << calcFloat(o, 5) << '\n';
std::cout << std::setprecision(9) << calcDouble(o, 5) << '\n';
}
Les résultats sont les suivants:
6254.77979
6254.7793
Ainsi, même si les entrées sont les mêmes dans les deux cas, vous obtenez un résultat différent en utilisant double
pour la sommation intermédiaire. Changer calcDouble
pour utiliser (double)w
ne change pas la sortie.
Ceci suggère que le calcul de (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w
est assez précis, mais l'accumulation d'erreurs pendant l'addition est ce qu'ils essaient d'éviter.
Cela est dû à la façon dont les erreurs sont propagées lorsque vous travaillez avec des nombres à virgule flottante. Citant The Floating-Point Guide: Error Propagation:
En général:
- La multiplication et la division sont des opérations « sûres »
- addition et la soustraction sont dangereuses, parce que lorsque le nombre de grandeurs différentes sont impliquées, les chiffres de la plus petite amplitude nombre sont perdus.
Donc, vous voulez le type supérieur de précision pour la somme, ce qui implique plus. Multiplier l'entier par un double
au lieu d'un float
n'importe pas autant: vous obtiendrez quelque chose qui est à peu près aussi précis que la valeur float
par laquelle vous commencez (tant que le résultat n'est pas très très grand ou très très petit).Mais additionner float
valeurs qui pourraient avoir des ordres de grandeur très différents, même lorsque les nombres individuels eux-mêmes sont représentables comme float
, va accumuler des erreurs et s'écarter de plus en plus de la vraie réponse.
Pour voir que dans l'action:
float f1 = 1e4, f2 = 1e-4;
std::cout << (f1 + f2) << '\n';
std::cout << (double(f1) + f2) << '\n';
ou équivalent, mais plus proche du code d'origine:
float f1 = 1e4, f2 = 1e-4;
float f = f1;
f += f2;
double d = f1;
d += f2;
std::cout << f << '\n';
std::cout << d << '\n';
Le résultat est:
10000
10000.0001
Ajout des deux flotteurs perd précision. Ajouter le flotteur à un double donne la bonne réponse, même si les entrées étaient identiques. Vous avez besoin de neuf chiffres significatifs pour représenter la valeur correcte, et c'est trop pour un float
.
Peut-être 'f [k] .w' est un' double'. –
@ tobi303 ehm [nope] (http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in-c) – justHelloWorld
@ FrançoisAndrieux alors quoi? :) Vous pouvez additionner deux doubles et enregistrer le résultat dans un flotteur sans aucun casting, non? – justHelloWorld