• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

C++ - Selbe Rechnung mit selben Operanden liefert unterschiedliche Ergebnisse?!

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
Hello again.

Ich verstehe gerade die (C++-) Welt nicht mehr. Wie bereits in meinem anderen Frage-Thread erwähnt, bastle ich gerade an einer Klasse, die zwischen dezimal, binär und hexadezimal hin- und herkonvertieren kann. Das allermeiste funktioniert tadellos. Nur die Umrechnung von dezimal in hexa bereitet mir gerade Kopfzerbrechen. An und für sich funktioniert auch diese Funktion gut, nur dass ich bei Eingaben ab einer bestimmten Größe immer leicht abweichende (also falsche) Ergebnisse bekomme.

Der relevante Code:
[src=cpp]string decToHex(long long lngDecInput)
{

int intMaxPower;
long long lngRemaining = lngDecInput;
stringstream sConv;
string strResult = "";
string strTmp = "";

if (lngDecInput <= 9)
{
sConv << lngDecInput;
sConv >> strResult;
sConv.clear();
return strResult;
}

for (int intPower = 0; ; intPower++)
{
if (pow(15.0, intPower) == lngDecInput)
{
intMaxPower = intPower;
break;
}
if (pow(15.0, intPower) > lngDecInput)
{
intMaxPower = intPower - 1;
break;
}
}

for (int intPower = intMaxPower; intPower >= 0; intPower--)
{
for (int intMultiplier = 15; intMultiplier >= 0; intMultiplier--)
{
if (intMultiplier * pow(15.0, intPower) <= lngRemaining)
{
switch (intMultiplier)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
sConv << intMultiplier;
sConv >> strTmp;
sConv.clear();
break;
case 10:
strTmp = "A";
break;
case 11:
strTmp = "B";
break;
case 12:
strTmp = "C";
break;
case 13:
strTmp = "D";
break;
case 14:
strTmp = "E";
break;
case 15:
strTmp = "F";
break;
}
// ############# DEBUGGING #############
cout << " +++ DEBUGGING +++" << endl <<
" lngRemaining = " << lngRemaining << endl <<
" substracting: " << intMultiplier * pow(15.0, intPower) << endl <<
" supp. NEW lngRemaining: " << lngRemaining - intMultiplier * pow(15.0, intPower) << endl;

lngRemaining = lngRemaining - intMultiplier * pow(15.0, intPower);

// ############# DEBUGGING #############
cout << " NEW lngRemaining = " << lngRemaining << endl;

strResult.append(strTmp);
strTmp = "";
break;
}
}
}

return strResult;

}[/src]

Die Einschübe mit Titel "Debugging" sind, wie unschwer zu erkennen, nur zu Debugging-Zwecken dort. Gebe ich nun zum Beispiel die Zahl 5.000.000 ein, bekomme ich folgende Ausgabe:

C++-Fehler.png

Alle paar Durchgänge bekomme ich solche Abweichungen. Entsprechend ist die Gesamtabweichung größer, je höher die umzuwandelnde Zahl. Und ich checke einfach nicht, woran das liegt. Die entsprechende Stelle im Code (Zeilen 76 bis 79) sind doch beide Male exakt identisch :confused: Ich habe absolut keine Ahnung, was da passiert. Hat da jemand von euch eine Idee?
 

alter_Bekannter

N.A.C.J.A.C.

Registriert
14 Juli 2013
Beiträge
4.823
Ort
Midgard
Was tut c++ wenn man versucht implizit zu konvertieren? Hier interessiert mich vor allem long/float/double/string/char[] und die Umgebung (Ich weiss es nicht.)


Dividierst du irgendwo?

Konvertierst du überall explizit?
 

Mäxchen

lustiger Kumpane

Registriert
14 Juli 2013
Beiträge
294
Ort
am liebsten im Zelt
Hallo. Hat zwar nichts direkt mit den beiden fragwürdigen Zeilen zu tun:
Die Basis des Hexadezimalsystems ist 16 und nicht 15. Die größte Ziffer ist zwar F=15, aber du müsstest schon überall "pow(16.0, intPower)" schreiben, denke ich, sonst landest du halt im 15er System und case F sollte niemals eintreten.
 

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
Wie es bei dir zu deinem offensichtlichen Fehler kommt, sehe ich jetzt allerdings auch nicht. Wundert mich ein wenig. Einen "Fehler" kann ich nicht erkennen.

Nichtsdestotrotz, habe ich da ein paar Anmerkungen zu deinem Code.

1) Wieso machst du es dir bei Ziffern von 0 bis 9 so umständlich und convertierst die über einen stringstream? ein std::to_string(x) sollte an der Stelle ebenfalls vollkommen ausreichend sein.
2) Wie @Mäxchen richtig erwähnt hat, ist die Basis von HEX die 16. Damit werden dann Ziffern von 0 bis 15 dargestellt, bis die nächste Potenz belegt wird.
3) Deine Funktion ist relativ lang. Es wäre daher sinnig diese in kleinere Teilfunktionen aufzuteilen. Beispielsweise lässt sich die for-Schleife wunderbar in eine Funktion auslagern, die den benötigten Exponenten zurückliefert.
4) Du verwendest in der entsprechenden Schleife die pow-Funktion. Ich habe mir nicht explizit nochmal die Dokumentation angeguckt, allerdings meine ich mich zu entsinnen, dass sie den gleichen Datentyp zurückgibt, wie der des Exponenten. Kläre mich bitte auf, falls das anders sein sollte. Demnach solltest du an der Stelle dein intPower ebenfalls zu einem long long machen.
5) Du verwendet dort ein std::string::append(). Das ist möglich, keine Frage. Allerdings habe ich es so in der Form bisher noch nie gesehen. Da du ohnehin einen anderen String anfügst (strTmp) würde ich dort den += Operator verwenden. Ganz simpel also ein strResult += strTmp;
6) Da du nur einzelne Zeichen anfügst, wäre es vermutlich Performancetechnisch sinniger nur den Datentyp char für dein strTmp zu verwenden. An dem resultat würde das nichts ändern. Lediglich bei einer häufigen Nutzung der Funktion, falls der Compiler das nicht sogar wegoptimieren würde.
7) Deine Schleife, mit der du die Bestandteile der HEX Zahl ermittelst, scheint mir unnötig kompliziert zu sein. Eine einfachere Lösung (für deine Funktion) findest du im Spoiler:

Beachte, dass die Funktion die Zahl von hinten an aufbaut. Das bedeutet, dass ganz am Schluss der String erst noch umgedreht werden muss. Ich habe an dieser Stelle die reverse Funktion aus algorithm benutzt. Dadurch, dass ich das Problem rückwärts durchgehe, verringert sich der Rechenaufwand, da kein pow benötigt wird, was (soweit ich mir das denke), ziemlich teuer in der Berechnung sein dürfte.
[src=cpp]
#include <iostream> // Nur verwendet für die Debug Ausgabe.
#include <string>
#include <algorithm> // Verwendet für die reverse-Funktion

std::string decToHex(long long lngDecInput)
{
// The array hexArrax has all representations of digits in the hex system.
// The index 0 points to number '0' and the index 15 to the number 'F'.
char hexArray[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

std::string output;
do
{
// % is the modulo operator. It devides the number with int devision and returns the remaining.
int remaining = lngDecInput % 16;

// Adding the hex number to the output
output += hexArray[remaining];

// Debug for your interest
//std::cout << "Output: " << output << std::endl;

// the same as lngDecInput = lngDecInput / 16
lngDecInput /= 16;
}
while(lngDecInput > 0);

// Swap the result to match the correct number.
reverse(output.begin(), output.end());
return output;
}

int main()
{
// 4C4B40
std::cout << decToHex(5000000) << std::endl;
// 75BCD15
std::cout << decToHex(123456789) << std::endl;
}
[/src]

Wie du siehst, kann man dein Switch-Case Statement deutlich kürzer schreiben, indem man direkt ein entsprechendes Array verwendet.

@alter_Bekannter: In seinem Codebeispiel wird überall nur implizit konvertiert. Das Verhalten davon vorauszusagen, finde ich selber auch nach Jahren in der C++-Welt weiterhin schwierig. Generell versucht der Compiler ein Mal zu konvertieren, um zu einem passenden Datentypen zu gelangen. Wie ich in der Auflistung oben aber angesprochen habe, könnte es hier ein Problem mit den Datentypen von pow geben. Allerdings habe ich mir immer gemerkt: Wenn man nicht explizit konvertiert, wird der größte beteiligte Datentyp verwendet. Falls das Ergebnis größer ist, gibt es einen Overflow (oder eine entsprechenden Fehler).
 
Zuletzt bearbeitet:

alter_Bekannter

N.A.C.J.A.C.

Registriert
14 Juli 2013
Beiträge
4.823
Ort
Midgard
Also ists vermutlich eine Konvertierung die schielfäuft, das führt zu solchen Problemen.

Also:
Runden ujm Genauigkeit und damit größe zu limitieren
Explizit konvertieren um Kontrolle über den Typ zu behalten


Im Grunde sollte man immer beides tun falls anwendbar. Zumindest wenn man Code haben will der sich vorhersehbar verhält.

Dein Code scheint ein schönes Beispiel dafür zu sein. Danke an Roin.
 

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.504
Ich rechne stark damit, dass es an Rundungsfehlern liegt, bzw. das du (aus versehen?) mit floats rechnest.
Datentyp float und vergleichbare Fließkommatypen sind dafür gedacht, ungefähre Werte zu speichern.
Intern wird IEEE 754 umgesetzt, d.h. unter anderem, dass die Zahlen normalisiert werden.

Normalisieren = Komma verschieben.
Das Format sieht so aus, also wie eine Zahl intern binär abgespeichert wird (bei floats):
V CCCCCCCC MMMMMMMMMMMMMMMMMMMMMMM
1 Vorzeichenbit, 8 Exponentbits (bzw. Charakteristik, hier gerade egal), 23 Mantissenbits = das was nicht zum Exponent gehört ;-).
Wenn er jetzt ne große Zahl speichert macht er folgendes:
Er wandelt sie erstmal ins Binärsystem um, da kommt dann sowas z.B. raus:
Zahl1: 1101,111 bei einer Kommazahl nahe Null = wenige Stellen vor dem Komma. Oder halt
Zahl2 111001101,111 bei einer Kommazahl.

Das wird jetzt normalisiert, d.h. das Komma wird so weit verschoben bis vorne nur noch eine eins Steht. Die Verschiebung wird als Exponent gespeichert.
Zahl1: 1,101111
Zahl2: 1,11001101111
Die 1 vor dem Komma wird verworfen (weil bei Normalisierung immer eine 1 vor dem Komma steht muss die nicht gespeichert werden)
Das hinter dem Komma wird in der Mantisse gespeichert. Erinnerung: wir haben immer genau 23 Bits für die Mantisse. Haben bei Zahl2 jetzt aber weniger Platz in der Mantisse übrig als bei Zahl1.
Das heißt unter anderem: Je größer die Zahl, desto weniger Bits = Genauigkeit bleiben für die Nachkommastellen übrig.
Die Zahl "5.000.000" hat binär nun genau 23 Stellen und verbraucht 22 Bits der zur Verfügung stehenden 23 Mantisse. Bliebe also z.B. wenig Platz für Nachkommagenauigkeit


Disclaimer: Bin grad nicht groß in deinen Code eingestiegen, aber das ist imHo woran man direkt denkt, wenn man fließkommazahl und Fehler nur bei großen Zahlen hört.
Und ja, du rechnest da mit floats. Weil du 15.0 schreibst.
Wenn du das durch 15 ersetzt bzw. halt mit Ganzzahlen rechnest (scheinst du eh nur zu wollen), könnte es sein, dass sich dein Problem direkt löst.

----------------------------------------

Btw. Menschen stöhnen immer das Studium sei so abstrakt und fern der praktischen Realität.
Sicher ist es hart theoretisch. Aber das hier ist ein schönes Beispiel, obiges ist erstes Semester Informatik, bei mir damals vierte oder fünfte Woche des Studiums.


PS.: Den Datentyp "strTmp" in der Variable zu kodieren ist bei einer statisch typisierten Sprache wie C++ komplett sinnfrei, weil der Typ (lassen wir Polymorphie außen vor, weil in dem Fall ergibt es noch weniger Sinn) zu jedem Zeitpunkt klar ist, dir klar ist, dem Compiler klar ist und eine IDE dir den auch anzeigen kann.
 
Zuletzt bearbeitet:

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
  • Thread Starter Thread Starter
  • #7
@Mäxchen: Hm, du scheinst Recht zu haben. Ich hatte gedacht, es geht ja um 16 verschiedene Zustände, also 0 bis 15. Aber bei der Umrechnung von dezimal in binär habe ich ja entsprechend auch 2er-Potenzen berechnet und nicht 1er-Potenzen :rolleyes:

@Roin: Vielen Dank für die guten Hinweise! Ich habe das jetzt auf Anhieb noch nicht alles verstanden, werde mir das aber später nochmal in Ruhe angucken. Weniger Rechenaufwand ist natürlich immer gut.

@BurnerR: Ich habe nur pow(15.0, x) verwendet, weil - soweit ich weiß - pow() als Basis immer ein double erwartet. Hatte ich so gelesen. Mit @Roins Variante brauche ich ja das pow() gar nicht mehr, soweit ich das eben gesehen habe. Insofern fällt das eh schonmal weg.

Ansonsten hatte ich an die Typkonvertierung noch nicht gedacht. Könnte wirklich sein, dass da irgendwo ein Problem auftaucht. Ich werde später erstmal eure allgemeinen Tipps einbauen und sehen, ob es dann funktioniert, und anschließend überlegen, ob ich einfach Roins Lösung implementiere.

Danke schonmal an alle :T

Dividierst du irgendwo?

Konvertierst du überall explizit?

In diesem Code-Abschnitt wird eigentlich nicht dividiert :unknown: Explizit konvertiert habe ich nicht überall. Das werde ich dann nachher nochmal probieren.

PS.: Den Datentyp "strTmp" in der Variable zu kodieren ist bei einer statisch typisierten Sprache wie C++ komplett sinnfrei, weil der Typ (lassen wir Polymorphie außen vor, weil in dem Fall ergibt es noch weniger Sinn) zu jedem Zeitpunkt klar ist, dir klar ist, dem Compiler klar ist und eine IDE dir den auch anzeigen kann.
Du meinst damit das strTmp? Würdest du das generell bei keiner Variablen machen? Ich finde das eigentlich ganz nett, das gleich auf den ersten Blick zu sehen. Natürlich, solange ich an dem Code arbeite, ist mir das alles klar, was da steht. Ich denke halt daran, wenn ich mal in längerer Zeit wieder reingucke, dass ich dann auch direkt weiß, worum es geht, und zwar auch, wenn ich nicht komplett von oben nach unten lese, sondern mittendrin einsteige.
 
Zuletzt bearbeitet:

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.504
Digging deeper:
Der Fehler wird höchstwahrscheinlich sein, was ich schrieb verbunden mit der pow funktion, die prinzipiell eine fließkommazahl zurückgibt und das korrekte Ergebnis nur approximiert:
https://www.geeksforgeeks.org/power-function-cc/
The pow() function takes ‘double’ as the arguments and returns a ‘double’ value. This functions does not always work for integers. One such example is pow(5, 2). When assigned to an integer, it outputs 24 on some compilers and works fine for some other compilers. But pow(5, 2) without any assignment to an integer outputs 25.
 

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
  • Thread Starter Thread Starter
  • #9
@BurnerR: Mhm, das ist sehr interessant. Irgendwie sowas (eine Funktion, die nicht ganz sauber funktioniert) hatte ich schon fast vermutet. Ich werde mal sehen, wie ich das umgehen kann. Vielleicht hilft tatsächlich eine explizite Typ-Konvertierung.
 

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.504
Siehe Link, solange du pow() für ganze Zahlen verwendest wirst du immer teilweise falsche (weil gerundete) Ergebnisse erhalten.

Du kannst deinen Fehler evtl. nachvollziehen, indem du in die Implentierung von pow() schaust.
Die soll wohl typischerweise so aussehen:

[src=cpp]double pow(double base, double exp)
{
return exp2(exp * log2(base));
}[/src]
Evtl. kannst du den Fehler schon reproduzieren, indem du obige pow Funktion testweise verwendest.
 
Zuletzt bearbeitet:

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
  • Thread Starter Thread Starter
  • #11
Okay. Dass pow() intern Logarithmen berechnet etc. war mir auch nicht bewusst. Dann ist das Argument mit den Rundungsfehlern doch wieder naheliegend.
Ich habe den Code jetzt erstmal weitestgehend nach euren bisherigen Tipps überarbeitet:

[src=cpp]string decToHex(long long lngDecInput)
{

int intMaxPower;
long long lngRemaining = lngDecInput;
stringstream sConv;
string strResult = "";
string strTmp = "";

if (lngDecInput <= 9)
{
return to_string(lngDecInput);
}

for (int intPower = 0; ; intPower++)
{
if ( (long long) pow(16.0, intPower) == lngDecInput)
{
intMaxPower = intPower;
break;
}
if ( (long long) pow(16.0, intPower) > lngDecInput)
{
intMaxPower = intPower - 1;
break;
}
}

for (int intPower = intMaxPower; intPower >= 0; intPower--)
{
for (int intMultiplier = 15; intMultiplier >= 0; intMultiplier--)
{
if (intMultiplier * (long long) pow(16.0, intPower) <= lngRemaining)
{
switch (intMultiplier)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
strTmp = to_string(intMultiplier);
break;
case 10:
strTmp = "A";
break;
case 11:
strTmp = "B";
break;
case 12:
strTmp = "C";
break;
case 13:
strTmp = "D";
break;
case 14:
strTmp = "E";
break;
case 15:
strTmp = "F";
break;
}

lngRemaining -= intMultiplier * (long long) ( pow(16.0, intPower) );

strResult += strTmp;
break;
}
}
}

return strResult;

}[/src]

(Ich habe also die Basis für die Umrechnung auf 16 geändert :rolleyes:, alle pow()'s explizit gecastet und den stringstream durch to_string() ersetzt. Sehr nützliche Funktion, kannte ich noch gar nicht :T)

Nachdem ich es mit einem Online Converter gegengetestet habe (auf die Idee hätte ich ja auch eher kommen können :rolleyes:), scheint jetzt soweit alles zu funktionieren, auch bei sehr großen Zahlen.

Hatte auch wie vorgeschlagen kurz ausprobiert, statt dem strTmp eine char-Variable zu benutzen, weil das natürlich weniger Speicher frisst. Leider gibt das dann beim Konvertieren in String teilweise Schwierigkeiten. Er hat dann die Zahlen 1 bis 9 offenbar nicht als Zeichen erkannt, sondern als ASCII-Werte, was zu irgendwelchen völlig unsinnigen Ausgaben führte. Das müsste ich mir nochmal ansehen, wie man das hinbekommt.

Die intMaxPower-Geschichte lagere ich noch eben in eine zusätzliche Funktion aus. Roins Alternative probiere ich dann später nochmal aus.
 

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
Ich habe noch einen Hinweis, an den ich heute Nacht gar nicht gedacht habe: Deine Funktion ist nur für nicht negative Zahlen implementiert. Meine Funktion allerdings auch. Entweder wandelst du eine negative Zahl vorher noch um und verarbeitest die korrekt ODER du nutzt als input ein unsigned long long (was übrigens auch nochmal etwa doppelt so groß ist).

Interessant wäre allerdings auch eine Funktion, die eine Zahl in einem String entgegennimmt ([-](0-9)*[.](0-9)*) und dann in beispielsweise Hex oder Binär übersetzt.
Ich bin mir zwar relativ sicher, dass es für derartige Dinge bereits Funktionen gibt, doch wäre das doch eine schöne Übung, findet ihr nicht?
 

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
  • Thread Starter Thread Starter
  • #13
Deine Funktion ist nur für nicht negative Zahlen implementiert.
Ja, das weiß ich. Wollte das erstmal so machen, weil ich nicht genau weiß wie negative Zahlen im Binär- bzw. Hexadezimal-System dargestellt werden, und mich dann evtl. später nochmal damit beschäftigen.
Das mit dem Unsigned long long wäre vermutlich ziemlich interessant. Ich habe nämlich gerade festgestellt, dass vor allem die Ausgabe meiner Funktion zur Berechnung von dezimal nach binär relativ schnell an ihre Grenzen stößt und einfach den höchstmöglichen Wert eines long long ausgibt.

Ich bin mir zwar relativ sicher, dass es für derartige Dinge bereits Funktionen gibt, doch wäre das doch eine schöne Übung, findet ihr nicht?

Wie gesagt, hinterher mache ich das bestimmt noch. Im Augenblick will ich das erstmal für positive Zahlen ans Laufen bekommen (was ja so gut wie geschafft ist).

[EDIT:]
Verdammt, jetzt weiß ich wieder, wieso ich die Funktionen nicht als unsigned definiert habe. Ich habe einen const short FORMAT_ERROR = -1 definiert, der zurückgegeben wird, wenn z.B. in der HEX-Eingabe unerlaubte Zeichen vorkommen :D
Da muss ich mir dann noch was zu überlegen.

Ansonsten bietet doch ein unsigned long Platz für eine genau so große höchste Zahl wie ein long long, verbraucht aber nur die Hälfte an Speicher, weil ja keine negativen Zahlen dargestellt werden können, richtig?
 
Zuletzt bearbeitet:

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
Ich habe einen const short FORMAT_ERROR = -1 definiert, der zurückgegeben wird, wenn z.B. in der HEX-Eingabe unerlaubte Zeichen vorkommen :D
Da muss ich mir dann noch was zu überlegen.
Das wäre genau der richtige Moment, um eine Exception zu werfen. throw std::invalid_argument("Invalid input");
Die würdest du dann weiter oben in deinem Programm mit einem catch(std::invalid_argument& e) abfangen und eine Fehlermeldung ausgeben lassen oder anderweitig verarbeiten.

Ansonsten bietet doch ein unsigned long Platz für eine genau so große höchste Zahl wie ein long long, verbraucht aber nur die Hälfte an Speicher, weil ja keine negativen Zahlen dargestellt werden können, richtig?
Das stimmt so nicht. Es gibt zwei Arten der Darstellung von negativen Zahlen in der Informatik. Die mir geläufigere ist, dass das erste bit als "Ich bin negativ"-Flag dient. Ansonsten gibt es noch eine mit Two-Complements. Da müsstest du mal ein wenig nach googlen, wenn dich das interessiert.
Nun zu deiner Frage: Wie an einem ähnlichen Beispiel auf stackoverflow zu sehen ist, ist unsigned etwa doppelt so groß wie signed. Gleiches gilt auch für alle anderen ähnlichen numerischen Typen wie unsigned long oder nur long.
 

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.504
Habe ein kleines Testprogramm geschrieben:
[src=cpp]#include <math.h>
#include <string>
#include <iostream>
#include <limits>

using namespace std;

string decToHex(long long lngDecInput)
{

int intMaxPower;
long long lngRemaining = lngDecInput;
string strResult = "";
string strTmp = "";

if (lngDecInput <= 9)
{
return to_string(lngDecInput);
}

for (int intPower = 0; ; intPower++)
{
if ( (long long) pow(16.0, intPower) == lngDecInput)
{
intMaxPower = intPower;
break;
}
if ( (long long) pow(16.0, intPower) > lngDecInput)
{
intMaxPower = intPower - 1;
break;
}
}

for (int intPower = intMaxPower; intPower >= 0; intPower--)
{
for (int intMultiplier = 15; intMultiplier >= 0; intMultiplier--)
{
if (intMultiplier * (long long) pow(16.0, intPower) <= lngRemaining)
{
switch (intMultiplier)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
strTmp = to_string(intMultiplier);
break;
case 10:
strTmp = "A";
break;
case 11:
strTmp = "B";
break;
case 12:
strTmp = "C";
break;
case 13:
strTmp = "D";
break;
case 14:
strTmp = "E";
break;
case 15:
strTmp = "F";
break;
}

lngRemaining -= intMultiplier * (long long) ( pow(16.0, intPower) );

strResult += strTmp;
break;
}
}
}

return strResult;

}

int main() {
int limit = std::numeric_limits<int>::max();
cout << "Testing up to: " << limit << endl;
for(int i=0; i<=limit; i++) {
if(i%10000000 == 0) {
cout << i << "..." << endl;
}
string converted_value = decToHex(i);
int my_result = stoi(converted_value,nullptr, 16);
if(my_result != i) {
cout << "Error: Tried " << i << ", but converted to " << my_result << endl;
}
}
}

[/src]
Grad mal durchlaufen lassen bis 300.000.000. Passt bis dahin ;-).
Habe schon eine Weile nicht in C++ programmiert, verzeiht mir die diversen "main()" Grausamkeiten :D.
Ich wundere mich allerdings das es hinhaut. Müsste pow() nicht weiterhin bei einigen Zahlen gerundete Ergebnisse liefern, die dann dazu führen, dass die berechnete Zahl zu niedrig ist?

Wieso liefern die diversen [src=cpp](long long) ( pow(16.0, intPower) )[/src]offenbar korrekte Ergebnisse?
 

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
@BurnerR: Könnte es vielleicht daran liegen, dass das Problem nur entsteht, wenn ein entsprechendes Fließkomma in einen String umgewandelt werden soll, dass dort anders gerundet wird, als wenn es in einen anderen numerischen Datentypen umgewandelt werden soll? Das scheint ja hier das Problem zu sein.
 

Kenobi van Gin

Brillenschlange

Registriert
14 Juli 2013
Beiträge
3.620
Ort
.\
  • Thread Starter Thread Starter
  • #18
Danke, @BurneR!
Ich habe mich auch gewundert, wieso die pow()-Funktion überhaupt mit log()'s rechnet. Ist das für die Berechnung von Potenzen von double-Werten notwendig? Wäre das nicht einfacher:
[src=cpp]
long long power(int base, int exponent)
{
long long result = 1;
for (int i = 1; i <= exponent; i++)
{
result *= base;
}

return result;
}[/src]

...oder wäre das bei großen Exponenten zu rechenaufwändig bzw. geht es mit log()'s schneller :unknown:

@Roin:
Mit exceptions habe ich unter C++ noch nicht gearbeitet. Werde ich mir wohl mal angucken. Ansonsten hatte ich mir überlegt, weil ich die ganzen Funktionen ja in einer Klasse zusammengefasst habe, dass ich einfach noch eine err-Variable hinzufüge, die dann eben verschiedene Zustände annehmen kann. Wäre das auch legitim? Oder machen exceptions (jetzt mal abgesehen vom Lerneffekt) mehr Sinn?
 
Zuletzt bearbeitet:

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.504
@BurnerR: Könnte es vielleicht daran liegen, dass das Problem nur entsteht, wenn ein entsprechendes Fließkomma in einen String umgewandelt werden soll, dass dort anders gerundet wird, als wenn es in einen anderen numerischen Datentypen umgewandelt werden soll? Das scheint ja hier das Problem zu sein.

Ich denke eigentlich aufgrund der Tatsache, das pow über den Logarithmus definiert ist sollte es eigentlich immer mal zu Problemen kommen.
Ich kriege hier in dem angegebenen Codeschnipsel allerdings auch stets das korrekte Ergebnis: https://codeforces.com/blog/entry/21844

Meine Vermutung ist so ein bisschen, dass sie das innerhalb der letzten 3 Jahre korrigiert haben z.B. indem sie nun doch pow(int, int) zur Verfügung stellen. Finde aber gerade keine Infos dazu leider :coffee:.

...oder wäre das bei großen Exponenten zu rechenaufwändig bzw. geht es mit log()'s schneller
Das ist jedenfalls auch meine erste Vermutung, kann mir aber auch vorstellen, dass die Genauigkeit schlechter werden kann insgesamt bei wiederholtem multiplizieren.
 

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
In deiner power Funktion musst du noch den Zähler in deiner Variable von power zu i umbenennen. Abgesehen davon, dass da unterschiedliche Namen von dir verwendet wurden (bestimmt ein Tippfehler) ist es unpraktisch eine Variable genau so wie die Funktion zu benennen.



...oder wäre das bei großen Exponenten zu rechenaufwändig bzw. geht es mit log()'s schneller :unknown:
Durch den Log sind die Zahlen zum Rechnen nunmal deutlich kleiner und das geht dann natürlich schneller. Das was du vor hast, geht zwar auch, allerdings ist das, wie du schon befürchtest vermeintlich deutlich langsamer.
Hier nochmal dein Code ein bisschen ausgebessert:
[src=cpp]
// unsigned int, da Negative Exponenten wie ein "Dividieren" sind. Das möchtest du mit deiner Funktion nicht, ansonste
// müsste sie komplexer werden, da diese Implementierung nur für nicht negative ganzzahlige Exponenten funktioniert.
// long long base da du vielleicht eine große Zahl noch ein weiteres Mal potenzieren möchtest.
long long power(long long base, unsigned int exponent)
{
// everything ^ 0 = 1
long long result = 1;
// Hier habe ich i = power gesetzt, wegen der oben genannten Namensüberschneidung
// Zudem habe ich ein unsigned int für i genutzt, damit es mit dem exponenten den gleichen Typ hat. Sonst kann ein Compiler ggf. eine Warnung ausgeben.
// Du möchtest genau ein mal pro Exponent durchlaufen. Daher nur < statt <=
for (unsigned int i = 0; i < exponent; i++)
{
result *= base;
}

return result;
}[/src]
Das sieht schlimmer aus, als es wirklich ist :D Sind nur kleine Anpassungen, damit du die für positive Zahlen so nutzen kannst.

Ansonsten hatte ich mir überlegt, weil ich die ganzen Funktionen ja in einer Klasse zusammengefasst habe, dass ich einfach noch eine err-Variable hinzufüge, die dann eben verschiedene Zustände annehmen kann.
Das kommt immer ganz auf das Design deiner Klasse an. Es gibt Libraries, die das genau so machen. Allerdings finde ich das eine sehr unschöne Lösung. Man kann in einer err-Variable vielleicht noch erweiterte Informationen speichern, was wirklich schief gelaufen ist (Fehlerbeschreibung). Allerdings sollte genau für sowas wie "Funktion mit Daten aufgerufen, die die nicht verarbeiten kann" eine Exception genutzt werden. Genau dafür sind die gedacht.

Dann gibt die Funktion nicht einen fehlerhaften Wert zurück oder ähnliches. Das Programm sagt allem aufrufenden "Hier ist kaputt. Mach was dagegen". Ist die elegantere Lösung dafür. Macht also abgesehen vom Lerneffekt auch Sinn, da der Programmierstil in C++ damit einfach schöner wird.
 
Oben