2016-04-26 4 views
1

Ich schreibe eine Funktion zum Konvertieren einer vom Benutzer bereitgestellten Zeichenfolge in ein Double. Es funktioniert ziemlich gut für bestimmte Werte, aber für andere nicht. Zum BeispielArithmetischer Fehler beim Konvertieren von Zeichenfolge in Double

string_to_double("123.45") = 123.45 
string_to_double(12345) = 12345 

aber

string_to_double(123.4567) = 123.457 

bin ich ziemlich sicher, dass dies eine Art von Abrundungsfehler ist, aber ich bin mit Annäherungen nicht noch verwende ich sehr kleine oder große Werte. Meine Frage ist zweifach, warum bekomme ich diese seltsamen Ergebnisse und wie kann ich meinen Code ändern, um genauere Ergebnisse zu erhalten? Ich mache das auch als eine persönliche Herausforderung, daher sind Vorschläge zur Verwendung von Methoden wie std::stod nicht hilfreich. Ich glaube, das Problem tritt in der zweiten for-Schleife auf, aber ich fand es ratsam, die gesamte Methode einzuschließen, denn wenn ich etwas verpasse, ist es nicht so viel zusätzlichen Code zu lesen.

My-Code

template <class T> 
double numerical_descriptive_measures<T>::string_to_double(std::string user_input) 
{ 
    double numeric_value = 0;//Stores numeric value of string. Return value. 
    int user_input_size = user_input.size(); 
    int power = 0; 
    /*This loop is for the characteristic portion of the input 
    once this loop finishes, we know what to multiply the 
    characterstic portion by(e.g. 1234 = 1*10^3 + 2*10^2 + 3*10^1 + 4) 
    */ 
    for(int i = 0;i < user_input_size;i++) 
    { 
     if(user_input[i] == '.') 
      break; 
     else 
      power++; 
    } 
    /*This loop is for the mantissa. If this portion is zero, 
    the loop doesn't execute because i will be greater than 
    user_input_size.*/ 
    for(int i = 0;i < user_input_size;i++) 
    { 
     if(user_input[i] != '.') 
     { 
      numeric_value += ((double)user_input[i] - 48.0)*pow(10,power-i-1); 
     } 
     else 
     { 
      double power = -1.0; 
      for(int j = i+1;j < user_input_size;j++) 
      { 
       numeric_value += ((double)user_input[j] - 48.0)*pow(10.0,power); 
       power = power-1.0; 
      } 
      break; 
     } 
    } 
    return numeric_value; 
} 
+2

Sind Sie sicher, dass der Wert wirklich 123,457 ist? Wie drucken Sie es? Wahrscheinlich wird die Druckfunktion gerade abgerundet. Warum erfinden Sie dieses Rad neu? –

+0

@JohnZwinck Ich drucke die Funktion mit Std :: Cout << Daten << Std :: Endl. Dieses Problem entsteht durch eine Statistikklasse, die ich schreibe. Ich bin zu diesem Zeitpunkt nur ein kleiner Student, also je mehr Code ich habe, desto besser. Dies geschah für Input-Tests (stellen Sie sicher, dass niemand irgendwelche doof Werte wie "finde den Durchschnitt von abc") einreicht. – Dunka

Antwort

2

Das Problem ist nicht, dass Sie den falschen Gleitkommawert produzieren, das Problem ist, dass man es mit unzureichender Genauigkeit drucken:

std::cout<<data<<std::endl 

Dieser Wille Nur ungefähr sechs Stellen der Genauigkeit drucken. Sie können std::setprecision oder andere Methoden verwenden, um mehr zu drucken.

+1

Der Code wird im allgemeinen falsche Werte erzeugen - siehe meine Antwort. –

2

Ihr Code erzeugt keinen falschen Wert für "123.4567", aber im Allgemeinen werden falsche Werte erzeugt. Zum Beispiel produziert string_to_double ("0,0012") (auf Visual Studio 2015)

0,0012000000000000001117161918529063768801279366016387939453125

aber die richtige Antwort ist

0,00119999999999999989487575735580549007863737642765045166015625

(würden Sie haben sie zu 17 signifikanten Stellen drucken um den Unterschied zu erkennen.)

Das Problem ist, dass Sie Fließkomma nicht verwenden können, um zu Fließkomma-i zu konvertieren t hat im Allgemeinen nicht genug Präzision.

(ich habe viel über diese auf meiner Website geschrieben, siehe zum Beispiel http://www.exploringbinary.com/quick-and-dirty-decimal-to-floating-point-conversion/ und http://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/.)