2016-12-08 5 views
10

Wir wissen, dass -2 * 4^31 + 1 = -9.223.372.036.854.775.807, der niedrigste Wert, den Sie lange speichern können, wie hier gesagt wird: What range of values can integer types store in C++. So habe ich diese Operation:lange lange Wert in Visual Studio

#include <iostream> 

unsigned long long pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -pow(4, 31) + 5 -pow(4,31); 
    std::cout << nr << std::endl; 
} 

Warum es -9.223.372.036.854.775.808 zeigen nicht statt -9.223.372.036.854.775.803? Ich verwende Visual Studio 2015.

+0

Kommentare sind nicht für längere Diskussionen; Diese Konversation wurde [in den Chat verschoben] (http://chat.stackoverflow.com/rooms/130291/discussion-on-question-by-peter-long-long-value-in-visual-studio). –

Antwort

14

Dies ist ein wirklich unangenehmes kleines Problem, das drei (!) Ursachen hat.

Erstens gibt es ein Problem, dass Fließkommaarithmetik ist ungefähr. Wenn der Compiler eine pow Funktion wählt, die float oder double zurückgibt, dann ist 4 ** 31 so groß, dass 5 weniger als 1ULP (Einheit mit der geringsten Genauigkeit) ist, also fügt es nichts hinzu (mit anderen Worten, 4,0 ** 31 + 5) == 4.0 ** 31). Multiplikation mit -2 kann ohne Verlust durchgeführt werden, und das Ergebnis kann in einem long long ohne Verlust als die falsche Antwort gespeichert werden: -9.223.372.036.854.775.808.

Zweitens kann ein Standard-Header andere Standard-Header enthalten, ist aber nicht erforderlich. Offensichtlich enthält die Visual Studio-Version von <iostream><math.h> (die pow im globalen Namespace deklariert), aber Code :: Blocks 'Version nicht.

Drittens der pow Funktion des OP ist nicht ausgewählt, weil er Argumente geht 4 und 31, die int beide vom Typ sind, und die deklarierte Funktion Argumente vom Typ unsigned. Seit C++ 11 gibt es viele Überladungen (oder eine Funktionsschablone) von std::pow. Diese geben alle float oder double zurück (es sei denn, eines der Argumente ist vom Typ long double - was hier nicht zutrifft).

Also eine Überladung von std::pow wird eine bessere Übereinstimmung sein ... mit einem doppelten Rückgabewerte, und wir erhalten Gleitkomma-Rundung.

Moral der Geschichte: Schreiben Sie nicht Funktionen mit dem gleichen Namen wie Standard-Bibliothek Funktionen, es sei denn Sie wirklich wissen, was Sie tun!

+0

"Visual Studio includes" sollte lesen "die Version von' ', die mit Visual Studio geliefert wird, enthält" –

+0

@LThode: Danke für Änderungen - viel verbessert! –

2

Der niedrigste lange Wert kann durch numeric_limits erhalten werden. Für lange lange es ist:

auto lowest_ll = std::numeric_limits<long long>::lowest(); 

die Ergebnisse:

-9223372036854775808

Die pow() Funktion, die Sie aufgerufen wird, ist nicht damit die beobachteten Ergebnisse. Ändern Sie den Namen der Funktion.

+0

Bitte beachten Sie die Kommentare unter der Frage. Er hat auch selbstgemachte unsigned Long Long-Funktion versucht. –

3

Visual Studio hat pow(double, int) definiert, die nur eine Konvertierung eines Arguments erfordert, während pow(unsigned, unsigned) Konvertierung beider Argumente erfordert, wenn Sie pow(4U, 31U) verwenden. Die Überladungsauflösung in C++ basiert auf den Eingaben - nicht auf dem Ergebnistyp.

+0

Zusätzlich, einschließlich * kann * (aber nicht erforderlich) die Standardversion von 'pow' deklarieren. Ich vermute, das Problem ist, dass es auf VS funktioniert und nicht auf Code: Blocks. –

1

Ihr erster Aufruf an pow verwendet die C-Standardbibliotheksfunktion, die auf Gleitkommazahlen arbeitet. Versuchen Sie, Ihre pow Funktion einen eindeutigen Namen zu geben:

unsigned long long my_pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -my_pow(4, 31) + 5 - my_pow(4, 31); 
    std::cout << nr << std::endl; 
} 

Dieser Code meldet einen Fehler: „einstellige Minusoperator auf unsigned Typ angewendet, führen immer noch ohne Vorzeichen“. Im Wesentlichen hat Ihr ursprünglicher Code, der eine Gleitkommafunktion genannt wurde, den Wert negiert, und er hat eine ganzzahlige Arithmetik angewendet, für die er nicht genügend Genauigkeit hatte, um die gesuchte Antwort zu geben (bei 19 Ziffern der Genauigkeit!). Um die Antwort, die Sie suchen, ändern Sie die Signatur:

long long my_pow(unsigned a, unsigned b); 

Das ist für mich in MSVC arbeitete ++ 2013. Wie in anderen Antworten erwähnt, Sie bekommen die Floating-Point pow weil Ihre Funktion erwartet unsigned und empfängt vorzeichenbehaftete Ganzzahlkonstanten. Das Hinzufügen von U zu Ihren Ganzzahlen ruft Ihre Version von pow auf.

+0

"unärer Minus-Operator, der auf vorzeichenlosen Typ angewandt wird, Ergebnis immer noch nicht signiert" ist KEIN Fehler. –

+0

Nehmen Sie es mit MSVC. :) Ich hatte keine speziellen Kompilierungsflags gesetzt. – cyberbisson

2

Die einzige mögliche Erklärung für das Ergebnis -9.223.372.036.854.775.808 ist die Verwendung der pow-Funktion aus der Standardbibliothek, die einen doppelten Wert zurückgibt. In diesem Fall wird 5 unter der Genauigkeit der Doppelberechnung liegen, und das Ergebnis wird genau -2 und umgewandelt in eine lange lange geben 0x8000000000000000 oder -9.223.372.036.854.775.808.

Wenn Sie die Funktion function verwenden, die ein unsigned long long zurückgibt, erhalten Sie eine Warnung, dass Sie ein unäres Minus auf einen vorzeichenlosen Typ anwenden und trotzdem eine ULL erhalten. Also sollte die gesamte Operation als unsigned long long ausgeführt werden und ohne Überlauf 0x8000000000000005 als vorzeichenloser Wert angegeben werden. Wenn Sie es auf einen vorzeichenbehafteten Wert umwandeln, ist das Ergebnis nicht definiert, aber alle Compiler, die ich kenne, verwenden einfach die vorzeichenbehaftete Ganzzahl mit der gleichen Darstellung, nämlich -9.223.372.036.854.775.803.

Aber es wäre einfach, die Berechnung zu machen, wie ohne Vorwarnung lange lange unterzeichnet nur mit:

long long nr = -1 * pow(4, 31) + 5 - pow(4,31); 

Als Zusätzlich haben Sie weder undefiniert Besetzung noch Überlauf hier so das Ergebnis perfekt pro definiert Standard vorausgesetzt, unsigned long lang ist mindestens 64 Bit.