C++ std::accumulate ne donne pas la somme prévue
double numbers[ ] = { 1, 0.5 ,0.333333 ,0.25 ,0.2, 0.166667, 0.142857, 0.125,
0.111111, 0.1 } ;
std::vector<double> doublenumbers ( numbers , numbers + 10 ) ;
std::cout << std::accumulate ( doublenumbers.begin( ) , doublenumbers.end( ) , 0 ) ;
Ce produit 1, ce qui est évidemment faux. Toutes les explications?
- +1, c'est un important gotcha qui me piquer plusieurs fois.
- aussi, le vecteur n'est pas nécessaire; vous pouvez utiliser des pointeurs comme des itérateurs:
std::accumulate ( numbers, numbers + sizeof numbers / sizeof *numbers, 0.0);
. [Dans le code réel, vous auriez probablement une constante ou une variablenum_numbers
plutôt quesizeof numbers / sizeof *numbers
] - Voir ma réponse pour un moyen facile de prévenir ce mal de tête à l'avenir.
Vous devez vous connecter pour publier un commentaire.
Vous devez écrire le code suivant:
Parce que le type 0 est
int
.Quand
std::accumulate
est instancié avec le type de la troisième argument estint
, alors il serait de convertir le côté droit de la somme. par exemple:Ce serait forcer une conversion de
double
àint
, au lieu de ce que vous pensiez de ce qui est à l'opposé.0.0
il pourrait encore être un certain décalage (pour des sommes importantes) en raison de la taille approximative de la représentation des nombres flottants.std::vector<float> v(1000, 1000.1f); std::cout << std::accumulate(v.begin(), v.end(), 0.0f);
rendements1.00011e+06
car float seulement a 5/6 chiffres de la valeur de précision sur ma machine.std::accumulate
, droit?Vous appelez
accumulate
avec0
comme leinit
argument, donc ça va s'accumuler à l'aide d'entiers des mathématiques. Utilisation0.0
à la place.ou
Le type de la "accumulateur" est une variable du type de la dernière argument de
std::accumulate
. Vous fourni0
comme un argument - unint
littérale - ce qui signifie que l'accumulateur de typeint
. Le "accumulation" se fait dans unint
accumulateur (c'est à dire arrondi àint
après chaque addition) et produitint
résultat. Dans ce cas, il est, apparemment,1
.std::accumulate
va commencer à somme le type qui est passé en 3ème argument, si vous passez un entier, le type de retour seraint
. Et dans ce cas implicitement converti endouble
.En C++11 et C++14, si vous voulez éviter le rétrécissement de conversion, vous pouvez créer un objet à l'aide direct de la liste d'initialisation:
Le compilateur va alors vous donner un avertissement disant que vous essayez de convertir de int en double et aussi à la ligne. Cela vous permettra d'économiser du temps de débogage. Et vous pouvez facilement le fixer de sorte qu'il devient correcte:
std::accumulate
prend deux paramètres du modèle dans ce cas. On peut définir l'itérateur de type, et de définir le type de retour. Si vous ne fournissez une, elle correspond au premier paramètre, et encore deviner la deuxième.