2010-12-08 9 views
5

Ich entwerfe eine Funktion, die eine Zeichenfolge in einen Float konvertiert. z.B. "45.5" = 45.5Konvertieren von String, in float (ohne atof) in C

Ich habe dies bisher. Aber es scheint nicht zu funktionieren. Denken Sie daran, wir können keine C-Bibliothek Funktionen wie atoi, atof oder sogar pow für diese Angelegenheit verwenden.

int str2float(char *s) 
{ 
    int num = 0; 
    int dec = 0; 
    double i = 1.0; 
    int ten = 1; 
    /***** ADD YOUR CODE HERE *****/ 

    for(; *s != '\0'; s++) 
    { 
     if (*s == '.'){ 
      for(; *s != '\0'; s++){ 
       dec = (dec * CONT) + (*s - '0'); 
       i++; 
      } 
     }else{ 
      num = (num * CONT) + (*s - '0'); 
     } 

    } 
    for(;i!=0;i--){ 
     ten *= 10; 
    } 
    dec = dec/(ten); 
    printf("%d", dec); 
    num += dec; 
    return num; 
} 
+0

Ich schlage vor, dass Sie printf() gesetzt Anweisungen in jedem einzelnen Schritt der Berechnung, so dass Sie sehen können, wie es weitergeht. Sie werden schnell sehen, was schief läuft. –

+17

Ich frage mich, warum Ihr str2float eine ganze Zahl zurückgibt! – Pirooz

+1

Und ich schlage vor, Sie durchlaufen es im Debugger. IMO ist es sogar besser als 'printf'. – detunized

Antwort

10

Hier ist mein Versuch:

float stof(const char* s){ 
    float rez = 0, fact = 1; 
    if (*s == '-'){ 
    s++; 
    fact = -1; 
    }; 
    for (int point_seen = 0; *s; s++){ 
    if (*s == '.'){ 
     point_seen = 1; 
     continue; 
    }; 
    int d = *s - '0'; 
    if (d >= 0 && d <= 9){ 
     if (point_seen) fact /= 10.0f; 
     rez = rez * 10.0f + (float)d; 
    }; 
    }; 
    return rez * fact; 
}; 
+0

Danke, das funktioniert. Ich musste die Dezimalstellen auf 4 stellen, damit die korrekte Ausgabe angezeigt wird. Danke noch einmal. –

+0

@user Wenn ich es ausführe, ist der Ausgang "24.5". – ruslik

+0

Ja, dein Recht! Mein Fehler, ich hatte eine temporäre var, die ein Int und nicht ein Doppel war, was die Vermischung verursacht. –

2

Ein mögliches Problem ist, dass s durch die äußere Schleife erhöht wird, bevor die Überprüfung, dass es nicht auf den NULL-Terminator zeigt.

for(; *s != '\0'; s++) 
{ 
     ... 
     for(; *s != '\0'; s++){ 
     ... 
     } 
     // inner loop is done now since we have *s=='\0' ... 
    ... 
    // ... but now we're going to increment s again at the end of the outer loop! 
} 

Sie müssen sowohl die innere und die äußere Schleife sofort beenden, nachdem der Nullabschluss getupft wird.

0

so etwas wie dies sollte es tun:

float str2float(char* s){ 
// solve for special cases where s begins with 0's or nun numeric values, or if s is NULL 
    float result = 0; 
    int decimalCount = 0, i = 0, decimalPointLoc = strlen(s); 
    for (; s[i] != '\0'; i++){ 
    if (s[i] == '.') decimalPointLoc = i; 
    if (i < decimalPointLoc) { result *= 10; result += (int)(s[i] + '0'); } 
    else { result += (float)(s[i] + '0')/(pow(i-decimalPointLoc,10)); } 
    } 
    return result; 
} 

Der Code nicht sehr sauber sein könnte und nicht unbedingt der beste Weg, es zu tun, aber Sie bekommen die Idee. pow (x, y) gibt x^y zurück und erfordert math.h und strlen (s) gibt die Größe von s zurück und erfordert string.h.

+0

Entschuldigung, ich kann Strlen nicht verwenden. Keine C-Bibliotheksfunktionen. –

+0

@ user373466: Sie können Ihre eigene Strlen-Funktion implementieren .. Es ist eine Frage von 3 Zeilen .. :) –

0
double Myatof(char str[]){ 

int len=0, n=0,i=0; 
float f=1.0,val=0.0; 

//counting length of String 
while(str[len])len++; 
//cheking for valid string 
if(!len)return 0; 

//Extracting Integer part 
while(i<len && str[i]!='.') 
    n=10*n +(str[i++]-'0'); 

//checking if only Integer 
if(i==len) return n; 
i++; 
while(i<len) 
{ 
    f*=0.1; 
    val+=f*(str[i++]-'0'); 
    } 
    return(val+n); 
} 
0
#define ZERO 48 
#define NINE 57 
#define MINUS 45 
#define DECPNT 46 

int strtoint_n(char* str, int n) 
{ 
    int sign = 1; 
    int place = 1; 
    int ret = 0; 

    int i; 
    for (i = n-1; i >= 0; i--, place *= 10) 
    { 
     int c = str[i]; 
     switch (c) 
     { 
      case MINUS: 
       if (i == 0) sign = -1; 
       else return -1; 
       break; 
      default: 
       if (c >= ZERO && c <= NINE) ret += (c - ZERO) * place; 
       else return -1; 
     } 
    } 

    return sign * ret; 
} 

float _float_fraction(char* str, int n) 
{ 
    float place = 0.1f; 
    float ret = 0.0f; 

    int i; 
    for (i = 0; i < n; i++, place /= 10) 
    { 
     int c = str[i]; 
     ret += (c - ZERO) * place; 
    } 
    return ret; 
} 
float strtoflt(char* str) 
{ 
    int n = 0; 
    int sign = 1; 
    int d = -1; 
    int ret = 0; 

    char* temp = str; 
    while (*temp != '\0') 
    { 
     switch (*temp) 
     { 
      case MINUS: 
       if (n == 0) sign = -1; 
       else return -1; 
       break; 
      case DECPNT: 
       if (d == -1) d = n; 
       else return -1; 
       break; 
      default: 
       if (*temp < ZERO && *temp > NINE) return -1; 
     } 
     n++; 
     temp++; 
    } 

    if (d == -1) 
    { 
     return (float)(strtoint_n(str, n)); 
    } 
    else if (d == 0) 
    { 
     return _float_fraction((str+d+1), (n-d-1)); 
    } 
    else if (sign == -1 && d == 1) 
    { 
     return (-1)*_float_fraction((str+d+1), (n-d-1)); 
    } 
    else if (sign == -1) 
    { 
     ret = strtoint_n(str+1, d-1); 
     return (-1) * (ret + _float_fraction((str+d+1), (n-d-1))); 
    } 
    else 
    { 
     ret = strtoint_n(str, d); 
     return ret + _float_fraction((str+d+1), (n-d-1)); 
    } 
} 
+2

Wenn jemand die Antwort wissen will, muss er auf Ihr Blog zugreifen. In Ihrem Blog stellen Sie einen Link zu SkyDrive zur Verfügung. Von dort müssen Sie eine Zip-Datei herunterladen und extrahieren, um die Lösung zu erhalten. Ist das nicht zu kompliziert? Fügen Sie die Quelle in Ihre Antwort ein !! Beachten Sie, dass [Link-only-Antworten] (http://meta.stackoverflow.com/tags/link-only-answers/info) nicht empfohlen werden. SO-Antworten sollten der Endpunkt einer Suche nach einer Lösung sein (vs Ein weiterer Zwischenstopp von Referenzen, die im Laufe der Zeit abgestanden werden. Bitte beachten Sie, dass Sie hier eine eigenständige Zusammenfassung hinzufügen und den Link als Referenz beibehalten. – Manuel

+0

ok fertig! und danke für deinen Rat. –

0

ich glaube, meine Lösung ist robuster.

Fühlen Sie sich frei, meinen Code herauszufordern. Hier ist der Link: https://github.com/loverszhaokai/Demo/blob/master/str_to_float/src/str_to_float.cc

Es folgt der Code:

#include <climits> 
#include <cmath> 
#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <iomanip> 
#include <string> 

using namespace std; 

int last_err_code = 0; 
// last_err_code = 0 when there is no error 
// 
// last_err_code = 1 when there is invalid characters, the valid characters 
//     are 0~9, '.', '+-' 
// 
// last_err_code = 2 when there is no integer before '.' or there is no integer 
//     after '.' 
//     e.g. ".123", "123.", "." 
// 
// last_err_code = 3 when there is more than one '.' 
//     e.g. "123..456", "123.4.56" 
// 
// last_err_code = 4 when the integer is bigger than FLOAT_MAX 
//     e.g. "1111111111111111111111111111111111111.23" 
// 

// Clear the left and right whitespace 
// e.g. " 123 456 " -> "123 456" 
char *trim(char *str) 
{ 
    while (*str == ' ' || *str == '\t') 
     str++; 

    char *start = str; 

    if (!(*str)) 
     return str; 

    char *end = str; 

    while (*str) { 
     if (*str != ' ' && *str != '\t') 
      end = str; 
     str++; 
    } 

    *(end + 1) = 0; 

    return start; 
} 

// String to Float 
float str_to_float(const char *pstr) 
{ 
    char *pstr_copy, *str; 
    // The sign of float, set -1 when the first character is '-' 
    int sign = 1; 
    // The value of integers 
    long long integer = 0; 
    // The value of decimals 
    double decimal = 0; 
    // The multiple of next decimal 
    double dec_num = 0.1; 
    // Has found dot '.' 
    bool has_dot = 0; 
    // Has integer 
    bool has_integer = 0; 

    if (pstr == NULL) 
     return 0; 

    pstr_copy = strdup(pstr); 
    str = trim(pstr_copy); 

    if (!(*str)) { 
     // " " 
     return 0; 
    } 

    if (*str == '+' || *str == '-') { 
     if (*str == '-') 
      sign = -1; 
     str++; 
    } 

    while (*str) { 

     if (*str >= '0' && *str <= '9') { 

      if (!has_dot) { 
       // "123" 
       if (!has_integer) 
        has_integer = 1; 

       integer *= 10; 
       integer += *str - '0'; 

       if (integer > (long long)INT_MAX) { 
        // e.g. "111111111111111111111111111111111.22" 
        last_err_code = 4; 
        return 0; 
       } 

      } else if (!has_integer) { 
       // ".123" 
       last_err_code = 2; 
       return 0; 
      } else { 
       // "123.456" 
       if (dec_num < (double)1e-10) { 
        // There are too many decimals, ignore the following decimals 
       } else { 
        decimal += (*str - '0') * dec_num; 
        dec_num *= 0.1; 
       } 
      } 

     } else if (*str == '.') { 

      if (has_dot) { 
       // e.g. "123...456" 
       last_err_code = 3; 
       return 0; 
      } 
      has_dot = 1; 

     } else { 
      // e.g. "123fgh?.456" 
      last_err_code = 1; 
      return 0; 
     } 

     str++; 
    } 

    if (has_dot && (!has_integer || dec_num == 0.1)) { 
     // e.g. ".123" or "123." or "." 
     last_err_code = 2; 
     return 0; 
    } 

    free(pstr_copy); 

    float ret = (float) integer + (float)decimal; 
    return ret * sign; 
} 

int main() 
{ 
    const struct TestCase { 
     const char *str; 
     const float ret; 
     int last_err_code; 
    } test_cases[] = { 
     // last_err_code != 0 
     { "abc", 0, 1 }, 
     { "123+.456", 0, 1 }, 
     { "++123.456", 0, 1 }, 

     { ".", 0, 2 }, 
     { ".123", 0, 2 }, 
     { "123.", 0, 2 }, 

     { "123..456", 0, 3 }, 
     { "123.4.56", 0, 3 }, 

     // Case #8: 
     { "1111111111111111111111111111111.456", 0, 4 }, 

     // last_err_code == 0 
     { "", 0, 0 }, 
     { "123.456", 123.456, 0 }, 
     // There are too many decimals 
     { "1.123456789", 1.12345678, 0 }, 
    }; 

    int errors = 0; 

    for (int iii = 0; iii < sizeof(test_cases)/sizeof(TestCase); iii++) { 
     const TestCase &tc = test_cases[iii]; 
     // Clear last_err_code 
     last_err_code = 0; 
     const float actual_ret = str_to_float(tc.str); 

     if (tc.ret != actual_ret || last_err_code != tc.last_err_code) { 

      errors++; 

      cout << "Case #" << iii << ": FAILED" << endl; 
      cout << "\tExpected ret=" << tc.ret << endl; 
      cout << "\tAcutal ret=" << actual_ret << endl; 
      cout << "\tExpected last_err_code=" << tc.last_err_code << endl; 
      cout << "\tAcutal last_err_code=" << last_err_code << endl; 
     } 
    } 

    if (errors == 0) 
     cout << "All test passed!" << endl; 
    else 
     cout << "There are " << errors << " cases failed." << endl; 

    return 0; 
} 
+0

Es ist eine schlechte Idee, das Ergebnis in einem Doppel zu akkumulieren, da jedes Mal, wenn du 'dezimal + = (* str - '0') * dec_num' hinzufügst, ein neuer Rundungsfehler hinzugefügt wird (und binär) Floats sind wirklich schlecht darin, negative Zehnerpotenzen darzustellen). Wenn Sie Ganzzahlen beibehalten, werden Sie nur einmal teilen, und das Ergebnis liegt innerhalb von 1 ULP des wahren Werts. Außerdem kann "strdup()" scheitern und es ist ein Overkill, nur weil es Platz zum Essen ist - warum nicht einfach die Anfangs- und Endzeiger halten? Schließlich ist ".123" absolut gültig. Ich habe diese Form irgendwann wirklich oft benutzt. –

1

Dies ist meine Lösung für atof Funktion.

#include<stdio.h> 
float my_a2f(char *); 

main() { 
    char ch[20]; 
    float i; 
    printf("\n enter the number as a string\n"); 
    scanf("%[^\n]", ch); 
    i = my_a2f(ch); 
    printf(" string =%s , float =%g \n", ch, i); 
} 

float my_a2f(char *p) { 
    // here i took another two variables for counting the number of digits in mantissa 
    int i, num = 0, num2 = 0, pnt_seen = 0, x = 0, y = 1; 
    float f1, f2, f3; 
    for (i = 0; p[i]; i++) 
    if (p[i] == '.') { 
     pnt_seen = i; 
     break; 
    } 
    for (i = 0; p[i]; i++) { 
    if (i < pnt_seen) num = num * 10 + (p[i] - 48); 
    else if (i == pnt_seen) continue; 
    else { 
     num2 = num2 * 10 + (p[i] - 48); 
     ++x; 
    } 
    } 
    // it takes 10 if it has 1 digit ,100 if it has 2 digits in mantissa 
    for (i = 1; i <= x; i++) 
    y = y * 10; 
    f2 = num2/(float) y; 
    f3 = num + f2; 
    return f3; 
} 
+1

OPs Beispiel '" 45.5 "' kommt mit "float = 45.15" hier auf. Bewertung vorschlagen – chux

+0

danke chux, ja, wenn wir die Anzahl der Ziffern in num2 zählen und entsprechend teilen. dann wird es in den meisten Fällen funktionieren, ich werde meine Antwort aktualisieren. überprüfen Sie bitte, ob es funktioniert. –

+0

Besser, float num, num2, y' zu verwenden, um Überlauf zu vermeiden. Besser, "0" als "48" zu verwenden. 'f1' nicht benutzt. – chux

0
#include<stdio.h> 
#include<string.h> 
float myAtoF(char *); 
//int myAtoI(char); 
void main(int argc,char **argv) 
{ 
float res; 
char str[10]; 
if(argc<2) 
{ 
    printf("Supply a floating point Data\n"); 
    return; 
} 
printf("argv[1] = %s\n",argv[1]); 
// strcpy(str,argv[1]); 
// printf("str = %s\n",str); 
    res=myAtoF(argv[1]); 
    printf("Res = %f\n",res); 
    } 
float myAtoF(char *str) 
{ 
    printf("str = %s\n",str); 
    int i,sum,total,index; 
    float res; 
    if(!strchr(str,'.')) 
    { 
     printf("Supply only real Data\n"); 
     return ; 
    } 
    i=0; 
    while(str[i]) 
    { 
     if(!str[i]>='0'&&str[i]<='9') 
     { 
      printf("Wrong Data Supplied\n"); 
      return;  

     } 
     if(str[i]=='.') 
     { 
      index=i; 
      i++; 
      continue; 

     } 
     total=str[i]-48; 
     if(i==0) 
     { 
      sum=total; 
      i++; 
      continue; 
     } 
     sum=sum*10+total; 
     i++; 
    } 
    printf("Integer Data : %d\n",sum); 
    index=(strlen(str)-1)-index; 
    printf("index : %d\n",index); 
    res=sum; 
    for(i=1;i<=index;i++) 
    { 
     res=(float)res/10; 
    } 
    return res; 
} 
0
double atof(char* num) 
{ 
    if (!num || !*num) 
     return 0; 
    double integerPart = 0; 
    double fractionPart = 0; 
    int divisorForFraction = 1; 
    int sign = 1; 
    bool inFraction = false; 
    /*Take care of +/- sign*/ 
    if (*num == '-') 
    { 
     ++num; 
     sign = -1; 
    } 
    else if (*num == '+') 
    { 
     ++num; 
    } 
    while (*num != '\0') 
    { 
     if (*num >= '0' && *num <= '9') 
     { 
      if (inFraction) 
      { 
       /*See how are we converting a character to integer*/ 
       fractionPart = fractionPart*10 + (*num - '0'); 
       divisorForFraction *= 10; 
      } 
      else 
      { 
       integerPart = integerPart*10 + (*num - '0'); 
      } 
     } 
     else if (*num == '.') 
     { 
      if (inFraction) 
       return sign * (integerPart + fractionPart/divisorForFraction); 
      else 
       inFraction = true; 
     } 
     else 
     { 
      return sign * (integerPart + fractionPart/divisorForFraction); 
     } 
     ++num; 
    } 
    return sign * (integerPart + fractionPart/divisorForFraction); 
} 
+0

Während dieses Code-Snippet die Frage lösen kann, [hilft eine Erklärung] (http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) wirklich, um die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in der Zukunft beantworten, und diese Leute könnten die Gründe für Ihren Codevorschlag nicht kennen. –

0

Meine Lösung:

float str2float (const char * str) { 
    unsigned char abc; 
    float ret = 0, fac = 1; 
    for (abc = 9; abc & 1; str++) { 
    abc = *str == '-' ? 
       (abc & 6 ? abc & 14 : (abc & 47) | 36) 
      : *str == '+' ? 
       (abc & 6 ? abc & 14 : (abc & 15) | 4) 
      : *str > 47 && *str < 58 ? 
       abc | 18 
      : (abc & 8) && *str == '.' ? 
       (abc & 39) | 2 
      : !(abc & 2) && (*str == ' ' || *str == '\t') ? 
       (abc & 47) | 1 
      : 
       abc & 46; 
    if (abc & 16) { 
     ret = abc & 8 ? *str - 48 + ret * 10 : (*str - 48)/(fac *= 10) + ret; 
    } 
    } 
    return abc & 32 ? -ret : ret; 
} 

Testen Sie

printf("%f\n", str2float("234.3432435543")); // 234.343246 
printf("%f\n", str2float("+234.3432435543")); // 234.343246 
printf("%f\n", str2float("-234.3432435543")); // -234.343246 
printf("%f\n", str2float(" + 234.3432435543")); // 234.343246 
printf("%f\n", str2float(".5")); // 0.500000 
printf("%f\n", str2float("- .5")); // -0.500000 

Btw, falls jemand es braucht, ist hier auch meine Lösung, die eine Zeichenfolge in eine integer zu konvertieren:

int str2int (const char *str) { 
    unsigned char abc; 
    int ret = 0; 
    for (abc = 1; abc & 1; str++) { 
    abc = *str == '-' ? 
       (abc & 6 ? abc & 6 : (abc & 23) | 20) 
      : *str == '+' ? 
       (abc & 6 ? abc & 6 : (abc & 7) | 4) 
      : *str > 47 && *str < 58 ? 
       abc | 10 
      : !(abc & 2) && (*str == ' ' || *str == '\t') ? 
       (abc & 23) | 1 
      : 
       abc & 22; 
    if (abc & 8) { 
     ret = ret * 10 + *str - 48; 
    } 
    } 
    return abc & 16 ? -ret : ret; 
} 
+0

Was haben Sie durch den Missbrauch des ternären Betreibers gewonnen? Die innersten sind in Ordnung, aber die äußeren sind sinnlos und dauern länger als die äquivalente "if"/"else" Kette. – rationalcoder

+0

Der ternäre Operator ist nur ein weiterer Weg, um das gleiche Ergebnis zu erhalten, mit der gleichen Leistung und mit einer besseren Klarheit des Codes (auf diese Weise wissen Sie, dass alle Bedingungen darauf gerichtet sind, der Variablen 'abc' einen Wert zuzuweisen eine 'if' /' else' Anweisung müssen Sie den ganzen Block lesen, um sicher zu sein. Was hätte ich ohne es gewonnen? – madmurphy

+0

Ich denke, wir haben nur unterschiedliche Vorstellungen über Lesbarkeit dann ... Das sieht aus wie jemand, der gerade gelernt hat, wie man den ternären Operator benutzt: P – rationalcoder

Verwandte Themen