2010-08-30 9 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) ; 

Dies erzeugt 1, die offensichtlich falsch ist. Irgendwelche Erklärungen?C++ Std :: Accumulate gibt nicht die erwartete Summe

+2

+1, ist dies ein wichtiger Gotcha, die mich mehrmals gebissen. –

+2

auch, der Vektor ist nicht notwendig; Sie können Zeiger als Iteratoren verwenden: 'std :: accumulate (Zahlen, Zahlen + Größe der Zahlen/Größe der * Zahlen, 0.0);'. [In echtem Code würden Sie wahrscheinlich eine Konstante oder Variable 'num_numbers' anstelle von' sizeof numbers/sizeof * numbers' haben. –

+0

Sehen Sie meine Antwort für eine einfache Möglichkeit, diese Kopfschmerzen in der Zukunft zu verhindern. –

Antwort

50

sollten Sie schreiben folgendes:

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

Da die Art von 0 int ist.

Wenn std::accumulate instanziiert ist mit dem Typ des dritten Arguments ist int, dann würde es die rechte Seite der Summe konvertieren. z.B .:

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

Diese eine Umwandlung von double zu int zwingen würde, statt dessen, was Sie davon dachten das Gegenteil.

+3

Was amüsant ist, ist, dass selbst bei Verwendung von '0.0' wegen der ungefähren Darstellung von Floating-Zahlen immer noch Diskrepanzen (bei großen Summen) auftreten können. 'std :: vector v (1000, 1000.1f); std :: cout << std :: akkumulieren (v.begin(), v.end(), 0.0f); 'ergibt' 1.00011e + 06', weil Float nur 5/6 Stellen Genauigkeit auf meinem Rechner hat. –

+1

Ich hatte gerade das gleiche Problem. Jetzt frage ich mich nur, wie man das aus der Dokumentation wissen soll (z. B. http://en.cppreference.com/w/cpp/algorithm/accumulate)? Dies ist eine ernsthafte Frage und keine Beschwerde. Ich würde mich wirklich gerne an C++ gewöhnen. – user463035818

+0

@MatthieuM. Dies ist kein besonderes Problem mit 'std :: accumum', oder? – Isaac

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

oder

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

Der Typ des "Speichers" Variable ist die Art des letzten Arguments von std::accumulate. Sie haben 0 als Argument angegeben - ein int Literal - was bedeutet, dass der Akkumulator den Typ int hat. Die "Akkumulation" erfolgt in einem int Akkumulator (d. H. Nach jeder einzelnen Summierung auf int gerundet) und erzeugt das Ergebnis int. In diesem Fall ist es anscheinend 1.

8

Du nennst accumulate mit 0 als init Argument, so dass es dann akkumulieren integer Mathematik verwenden. Verwenden Sie stattdessen 0.0.

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

Mein Verdacht ist, dass einige Template-Parameter verschlungen wurden. Schließen Sie Ihren Code in Ticks (') ein, um das zu verhindern. –

+1

Willkommen bei Stack Overflow. Das wäre eine bessere Antwort, wenn Sie erwähnt hätten, warum das funktioniert, und auch, wenn Sie darauf aufmerksam gemacht hätten, was genau Sie geändert haben. Es ist der Unterschied, einem Mann einen Fisch zu geben und ihn zu fischen. –

+0

Ahh, wenn du das wolltest, dann bin ich mir nicht sicher, ob das funktioniert. 'std :: accumum' nimmt in diesem Fall zwei Template-Parameter. Eine zum Definieren des Iteratortyps und eine zum Definieren des Rückgabetyps. Wenn Sie nur eins angeben, wird es mit dem ersten Parameter übereinstimmen und immer noch den zweiten erraten. –

2

std::accumulate beginnt die Art zusammenzufassen, die als drittes Argument übergeben wird, wenn Sie eine ganze Zahl übergeben, wird der Rückgabetyp int sein. Und in diesem Fall implizit in eine double umgewandelt.

In C++ 11 und C++ 14, wenn Sie Umwandlung Verengung verhindern möchten, können Sie ein Objekt mit Direkt-list-Initialisierung erstellen:

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

Der Compiler wird Ihnen dann eine Warnung, die besagt, dass Sie versuchen, von int in double und auch in welcher Zeile zu konvertieren. Dies spart Ihnen Zeit für die Fehlersuche. Und man kann es leicht beheben, so dass es richtig wird:

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

Für die Flout-Ausgabe können wir 0.0f verwenden. – user1436187

Verwandte Themen