2010-08-30 2 views
27
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) ; 

Ceci produit 1, ce qui est évidemment faux. Des explications?C++ std :: accumulate ne donne pas la somme attendue

+2

+1, c'est une Gotcha importante qui m'a mordu plusieurs fois. –

+2

également, le vecteur n'est pas nécessaire; vous pouvez utiliser des pointeurs comme itérateurs: 'std :: accumulate (nombres, nombres + sizeof nombres/sizeof * nombres, 0.0); [En code réel, vous auriez probablement une constante ou une variable 'num_numbers' plutôt que' sizeof numbers/sizeof * numbers'] –

+0

Voir ma réponse pour un moyen facile d'éviter ce mal de tête à l'avenir. –

Répondre

50

Vous devriez écrire:

std::cout << 
std::accumulate (doublenumbers.begin() , doublenumbers.end() , 0.0) ; 

Parce que le type de 0 est int. Lorsque std::accumulate est instancié avec le type du troisième argument est int, alors il convertirait le côté droit de la somme. .: par exemple

result += *iter; 
// int += double 

Cela forcerait une conversion de double à int, au lieu de ce que vous pensez qui est le contraire.

+3

Ce qui est amusant est que même en utilisant '0.0' il pourrait encore y avoir des différences (pour les sommes importantes) à cause de la représentation approximative des nombres flottants. 'std :: vecteur v (1000, 1000.1f); std :: cout << std :: accumulate (v.begin(), v.end(), 0.0f); 'donne' 1.00011e + 06' car float n'a que 5/6 chiffres de précision sur ma machine. –

+1

Je viens d'avoir le même problème. Maintenant, je me demande juste, comment on est censé le savoir à partir de la documentation (par exemple http://en.cppreference.com/w/cpp/algorithm/accumulate)? C'est une question sérieuse et non une plainte. Je voudrais vraiment être plus habitué au C++. – user463035818

+0

@MatthieuM. Ce n'est pas un problème particulier avec 'std :: accumulate', non? – Isaac

5
std::accumulate (doublenumbers.begin() , doublenumbers.end() , .0) ; 

ou

std::accumulate (doublenumbers.begin() , doublenumbers.end() , (double) 0) ; 

Le type de la variable "accumulateur" est le type du dernier argument de std::accumulate. Vous avez fourni 0 comme argument - un littéral int - ce qui signifie que l'accumulateur aura le type int. L'accumulation est effectuée dans un accumulateur int (c'est-à-dire arrondi à int après chaque addition individuelle) et produit le résultat int. Dans ce cas, il est, apparemment, 1.

8

Vous appelez accumulate avec 0 comme l'argument init, donc il va s'accumuler en utilisant des maths entiers. Utilisez 0.0 à la place.

1
std::accumulate<double> (doublenumbers.begin(), doublenumbers.end(), 0); // also works 
+1

Mon soupçon est que certains paramètres de modèle ont été englouties. Entourez votre code en ticks (') pour éviter que cela n'arrive. –

+1

Bienvenue dans Stack Overflow. Ce serait une meilleure réponse si vous aviez mentionné * pourquoi * cela fonctionne, et aussi si vous aviez attiré l'attention sur ce que vous avez exactement changé. C'est la différence entre donner un poisson à un homme et lui apprendre à pêcher. –

+0

Ahh, si c'est ce que vous vouliez, alors je ne suis pas certain que ça marchera. 'std :: accumulate' prend deux paramètres de template dans ce cas. Un pour définir le type d'itérateur, et un pour définir le type de retour. Si vous n'en fournissez qu'une, elle correspondra au premier paramètre, et devinera encore la seconde. –

2

std::accumulate va commencer à résumer le type qui est passé comme 3ème argument si vous passez un entier, le type de retour sera int. Et dans ce cas implicitement converti en double.

En C++ 11 et 14 C++, si vous voulez éviter la conversion rétrécissant, vous pouvez créer un objet à l'aide directe-list-initialisation:

double sum { std::accumulate(doublenumbers.begin(), doublenumbers.end(), 0) }; 

Le compilateur vous donnera un avertissement indiquant que vous essayez de convertir d'int en double et aussi à quelle ligne. Cela vous permettra d'économiser du temps de débogage. Et vous pouvez facilement résoudre ce problème afin qu'il devienne correct:

double sum { std::accumulate(doublenumbers.begin(), doublenumbers.end(), 0.0) }; 
+0

Pour la sortie floutée, nous pouvons utiliser 0.0f. – user1436187

Questions connexes