2016-09-23 93 views
0

我目前有以下代碼。但是,如果輸入類似3.1?43的東西,它仍然被標記爲浮動。我知道我沒有正確地檢查後。但我不確定如何檢查。檢查來自main的字符串是否是有效的浮點數

int floatNum(char *s) { 
    char *ptr = s; 
    char *ep = NULL; 
    long i = strtol(s, &ep, 0); 

    //check if converted to long int 
    if (!*ep) { 
     return false; 
    } 

    //Check if char 
    if (*ep == 'e' || *ep == 'E' || *ep == '.') { 
     return true; 
    } 

    return false; 
} 

謝謝你,這是我的解決方案。由於我的項目設置的方式,我只希望它返回true,如果它是一個有效的浮點數,否則返回false。

int floatNum(char *s) { 
    const char *ptr = s; 
    double x = strtod(ptr, &s); 

    //check if converted to long int 
    if (*s == 0) { 
     return true; 
    } 
    else { 
     return false; 
    } 
    return false; 
} 
+5

您可能應該使用'strtod'。 – keltar

+0

@keltar謝謝你的建議。 – terribleProgrammer

+0

@terribleProgrammer更好地張貼您的答案作爲下面的答案比改變你的文章。就目前而言,這個答案有問題。 – chux

回答

1

要測試字符串是否爲有效的浮點值,請使用strtod。本函數解析一個可選的小數點和可選的指數符(「E」或「E」)數字字符串:

char *p; 

errno = 0; 
double f = strtod(str,&p); 
if (errno) { 
    printf("conversion failed"); 
} 
if (strlen(str)==0) { 
    printf("empty string\n"); 
} else { 
    printf("f=%f\n", f); 
    if (*p == 0) { 
     printf("entire string valid\n"); 
    } else { 
     printf("extra characters: %s\n", p); 
    } 
} 
+2

我們應該知道'strtod()'將接受並轉換無窮和NaN的文本表示以及十六進制浮點表示,所以如果需要拒絕這些輸入和返回值,應該進一步測試輸入和返回值。 –

+0

難道沒有辦法用strtol來處理它嗎? – terribleProgrammer

+0

@terribleProgrammer好奇,你爲什麼要使用'strtol()'? – chux

0

檢查正確性只是很難,因爲完全解析它,沒有捷徑。我正在修改我的任意精度庫,並且仍然是一個邪惡混亂的東西之一就是字符串解析,所以我放下並做了一個(希望)恰當地實現了有限狀態機(帶有一些快捷方式,被承認)。近500行代碼。我使用了簡單的'雙測試(不適用,錯誤太大!),以便能夠運行一些簡單的測試。

請注意,我也允許八進制數字具有相同的二進制指數和指數標記p作爲標準化的十六進制數字,strtod沒有。

哦,我敢肯定它有一些錯誤。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

// for pow() only 
#include <math.h> 


#ifdef DEBUG 
# define PUTS(x) puts(x) 
#else 
# define PUTS(x) 
#endif 


#define FSM_OK  1 
#define FSM_ERROR 0 

/* 
    EBNF (ISO 14977) for the real numbers this code should be able to treat correctly. 

    (* sign *) 
    sign = '+'|'-'; 

    (* numbers starting with zero are octal, hence the special treatment for zero *) 
    zero = '0'; 

    (* binary digit *) 
    bindig = '1'; 
    (* binary integer *) 
    binint = bindig , [{(zero | bindig)}]; 

    (* octal digit *); 
    octdig = bindig|'2'|'3'|'4'|'5'|'6'|'7'; 
    (* octal integer *) 
    octint = octdig , [{(zero | octdig)}]; 

    (* decimal digit *) 
    decdig = octdig|'8'|'9'; 
    (* decimal integer *) 
    decint = decdig , [{(zero | decdig)}]; 

    (* hexadecimal digit *) 
    hexdig = decdig |'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'e'|'f'; 
    (* hexadecimal integer *) 
    hexint = hexdig , [{(zero | hexdig)}]; 

    (* prefix for the base; inclusion of octal numbers made it interesting *) 
    prefix = '0'; 
    (* Implementing only the four main bases has been deemed sufficient *) 
    binbase = prefix , ('B'|'b'); 
    octbase = prefix , octdig; 
    hexbase = prefix , ('X'|'x'); 

    (* decimal point *) 
    decpoint = '.'; 

    (* exponents *) 
    expobin = ('P'|'p'), [sign], decint; 
    expodec = ('E'|'e'), [sign], decint; 

    (* thousands separator *) 
    (* tsep = ','|'\''|'_'; *) 

    (* "xyz", "xyz.", "xyz.zyx", ".zyx" *) 
    stubrealbin = binint | (binint, decpoint, [binint]) | (decpoint, binint); 
    stubrealoct = octint | (octint, decpoint, [octint]) | (decpoint, octint); 
    stubrealdec = decint | (decint, decpoint, [decint]) | (decpoint, decint); 
    stubrealhex = hexint | (hexint, decpoint, [hexint]) | (decpoint, hexint); 

    fullrealbin = [sign], binbase, stubrealbin, [expobin]; 
    fullrealoct = [sign], octbase, stubrealoct, [expobin]; 
    fullrealdec = [sign],   stubrealdec, [expodec]; 
    fullrealhex = [sign], hexbase, stubrealhex, [expobin]; 

    (* "InF" and "NaN" should be case insensitive in praxi *) 
    real = ([sign], "Inf") | "NaN" | fullrealbin | fullrealoct | fullrealdec | fullrealhex; 



    Input characters (grouped for clarity. Case sensitive for brevity only!). 
    The input is case insensitive, so 'e', 'E', 'b', and 'B' are only 
    distinguishable by context: once you have hexdigits 'e' and 'b' are hexdigits 
    and a following sign must trigger an error 

    '0' 
    '1'      = bin 
    '2'|'3'|'4'|'5'|'6'|'7' = oct 
    '8'|'9'     = dec 
    'A'| |'C'|'D' |'F' = hex 
    'x' 
    'b' 
    'e' 
    'p' 
    '.' 
    '+' | '-'    = sgn 

    Any other input character is not valid (state: ERROR), "Inf" and "NaN" get handled 
    elsewhere to keep things simple. 


    States 

    START 
    SIGN 
    PREFIX 
    DECPOINT 
    DECFRAC 
    BINBASE 
    OCTBASE 
    DECBASE 
    HEXBASE 
    BINFRAC 
    OCTFRAC 
    HEXFRAC 
    EXPOMARK 
    EXPOSIGN 
    EXPONENT 
    ERROR 

    Transition table 

    START '0' it is a prefix    PREFIX 
      bin it is a binary digit  DECBASE 
      oct it is an octal digit  DECBASE 
      dec it is a decimal digit  DECBASE 
      hex it is a hexadecimal digit HEXBASE 
      'x' cannot be     ERROR 
      'b' it is a hexadecimal digit HEXBASE 
      'e' it is a hexadecimal digit HEXBASE 
      'p' cannot be     ERROR 
      '.' a decimal point    DECPOINT (!) 
      sgn a sign      SIGN 

    SIGN '0' it is a prefix    PREFIX 
      bin it is a binary digit  DECBASE 
      oct it is an octal digit  DECBASE 
      dec it is a decimal digit  DECBASE 
      hex it is a hexadecimal digit HEXBASE 
      'x' cannot be     ERROR 
      'b' it is a hexadecimal digit HEXBASE 
      'e' it is a hexadecimal digit HEXBASE 
      'p' cannot be     ERROR 
      '.' a decimal point    DECPOINT (!) 
      sgn a sign      ERROR 

    PREFIX '0' zero      OCTBASE 
      bin binary digit    OCTBASE 
      oct octal digit     OCTBASE 
      dec cannot be     ERROR 
      hex cannot be     ERROR 
      'x' hexadecimal number   HEXBASE 
      'b' binary number    BINBASE 
      'e' cannot be     ERROR 
      'p' cannot be     ERROR 
      '.' decimal point    DECFRAC 
      sgn cannot be     ERROR 

    The only place, that is still lingering in limbo is the decimal point. We 
    need to fork again, but we do not know in which base the integral part (if one 
    exists at all) is, so we still take "decimal" to be the default base. 


    DECPOINT '0' binary digit    DECFRAC 
      bin binary digit    DECFRAC 
      oct octal digit    DECFRAC 
      dec decimal digit    DECFRAC 
      hex hexadecimal digit   HEXFRAC 
      'x' cannot be     ERROR 
      'b' hexadecimal digit   HEXFRAC 
      'e' hexadecimal digit   HEXFRAC 
      'p' binary exponent   EXPOMARK 
      '.' decimal point    ERROR 
      sgn cannot be     ERROR 

    A fractional part in decimal base is simple enough 

    DECFRAC '0' binary digit    DECFRAC 
      bin binary digit    DECFRAC 
      oct octal digit    DECFRAC 
      dec decimal digit    DECFRAC 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' exponent delimiter   EXPOMARK 
      'p' cannot be     ERROR 
      '.' cannot be     ERROR 
      sgn cannot be     ERROR 

    All other fractions need a base, so handle bases first 

    BINBASE '0' binary digit    BINBASE 
      bin binary digit    BINBASE 
      oct cannot be     ERROR 
      dec cannot be     ERROR 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' exponent delimiter   EXPOMARK 
      '.' decimal point    BINFRAC 
      sgn cannot be     ERROR 

    OCTBASE '0' binary digit    OCTBASE 
      bin binary digit    OCTBASE 
      oct octal digit    OCTBASE 
      dec cannot be     ERROR 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' exponent delimiter   EXPOMARK 
      '.' decimal point    OCTFRAC 
      sgn cannot be     ERROR 

    HEXBASE '0' binary digit    HEXBASE 
      bin binary digit    HEXBASE 
      oct octal digit    HEXBASE 
      dec decimal digit    HEXBASE 
      hex hexadecimal digit   HEXBASE 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' exponent delimiter   EXPOMARK 
      '.' decimal point    HEXFRAC 
      sgn cannot be     ERROR 

    It is easy now to fill the gaps. 

    BINFRAC '0' binary digit    BINFRAC 
      bin binary digit    BINFRAC 
      oct cannot be     ERROR 
      dec cannot be     ERROR 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' exponent delimiter   EXPOMARK 
      '.' cannot be     ERROR 
      sgn cannot be     ERROR 

    OCTFRAC '0' binary digit    OCTFRAC 
      bin binary digit    OCTFRAC 
      oct octal digit    OCTFRAC 
      dec cannot be     ERROR 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' exponent delimiter   EXPOMARK 
      '.' cannot be     ERROR 
      sgn cannot be     ERROR 

    HEXFRAC '0' binary digit    HEXFRAC 
      bin binary digit    HEXFRAC 
      oct octal digit    HEXFRAC 
      dec decimal digit    HEXFRAC 
      hex hexadecimal digit   HEXFRAC 
      'x' cannot be     ERROR 
      'b' hexadecimal digit   HEXFRAC 
      'e' hexadecimal digit   HEXFRAC 
      'p' exponent delimiter   EXPOMARK 
      '.' decimal point    ERROR 
      sgn cannot be     ERROR 

    The exponent is always encoded in decimal base (IEEE 754 (ISO/IEC 60559) sec. 5.12.3 for 
    the hexadecimal example) 

    EXPOMARK '0' binary digit    EXPONENT 
      bin binary digit    EXPONENT 
      oct octal digit    EXPONENT 
      dec decimal digit    EXPONENT 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' cannot be     ERROR 
      '.' cannot be     ERROR 
      sgn sign      EXPOSIGN 

    EXPOSIGN '0' binary digit    EXPONENT 
      bin binary digit    EXPONENT 
      oct octal digit    EXPONENT 
      dec decimal digit    EXPONENT 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' cannot be     ERROR 
      '.' cannot be     ERROR 
      sgn sign      ERROR 

    EXPONENT '0' binary digit    EXPONENT 
      bin binary digit    EXPONENT 
      oct octal digit    EXPONENT 
      dec decimal digit    EXPONENT 
      hex cannot be     ERROR 
      'x' cannot be     ERROR 
      'b' cannot be     ERROR 
      'e' cannot be     ERROR 
      'p' cannot be     ERROR 
      '.' cannot be     ERROR 
      sgn sign      ERROR 

    ERROR nothing follows an error, only more ERROR 

    Nothing marking an end-state, it is just the end of the input buffer 


ZERO  '0' 
BINDIG '1' 
OCTDIG '2'|'3'|'4'|'5'|'6'|'7' 
DECDIG '8'|'9' 
HEXDIG 'A'| |'C'|'D' |'F' 
PREHEX 'x' 
PREBIN 'b' 
EXPDEC 'e' 
EXPBIN 'p' 
DECPNT '.' 
SGNCHR  '+' | '-' 

      ZERO BINDIG OCTDIG DECDIG HEXDIG PREHEX PREBIN EXPDEC EXPBIN DECPNT SGNCHR 
START  PREFIX DECBASE DECBASE DECBASE HEXBASE ERROR HEXBASE HEXBASE ERROR DECPOINT SIGN 
SIGN  PREFIX DECBASE DECBASE DECBASE HEXBASE ERROR HEXBASE HEXBASE ERROR DECPOINT ERROR 
PREFIX OCTBASE OCTBASE OCTBASE ERROR ERROR HEXBASE BINBASE ERROR ERROR DECFRAC ERROR 
DECPOINT DECFRAC DECFRAC DECFRAC DECFRAC HEXFRAC ERROR HEXFRAC HEXFRAC EXPOMARK ERROR ERROR 
DECFRAC DECFRAC DECFRAC DECFRAC DECFRAC ERROR ERROR ERROR EXPOMARK ERROR ERROR ERROR 
BINBASE BINBASE BINBASE ERROR ERROR ERROR ERROR ERROR ERROR EXPOMARK BINFRAC ERROR 
OCTBASE OCTBASE OCTBASE OCTBASE ERROR ERROR ERROR ERROR ERROR EXPOMARK OCTFRAC ERROR 
DECBASE DECBASE DECBASE DECBASE ERROR ERROR ERROR ERROR EXPOMARK ERROR DECFRAC ERROR 
HEXBASE HEXBASE HEXBASE HEXBASE HEXBASE HEXBASE ERROR ERROR ERROR EXPOMARK HEXFRAC ERROR 
BINFRAC BINFRAC BINFRAC ERROR ERROR ERROR ERROR ERROR ERROR EXPOMARK ERROR ERROR 
OCTFRAC OCTFRAC OCTFRAC OCTFRAC ERROR ERROR ERROR ERROR ERROR EXPOMARK ERROR ERROR 
HEXFRAC HEXFRAC HEXFRAC HEXFRAC HEXFRAC HEXFRAC ERROR HEXFRAC HEXFRAC EXPOMARK ERROR ERROR 
EXPOMARK EXPON'T EXPON'T EXPON'T EXPON'T ERROR ERROR ERROR ERROR ERROR ERROR EXPOSIGN 
EXPOSIGN EXPON'T EXPON'T EXPON'T EXPON'T ERROR ERROR ERROR ERROR ERROR ERROR ERROR 
EXPONENT EXPON'T EXPON'T EXPON'T EXPON'T ERROR ERROR ERROR ERROR ERROR ERROR ERROR 
ERROR  ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR 
*/ 


#if !(defined _BSD_SOURCE || defined _DEFAULT_SOURCE || _POSIX_C_SOURCE >= 200112L) 
# include <ctype.h> 
static int strncasecmp(const char *s1, const char *s2, size_t n) 
{ 
    char c1 = 0; 
    char c2 = 0; 

    while (n--) { 
    c1 = tolower(*s1); 
    c2 = tolower(*s2); 

    if (c1 != c2) { 
     break; 
    } 
    if (c1 == '\0') { 
     break; 
    } 
    s1++; 
    s2++; 
    } 
    return (int) (c1 - c2); 
} 
#endif 

#ifdef _MSC_VER 
#define strncasecmp(x,y,z) _strnicmp(x,y,z) 
#endif 


enum fsm_input { 
    ZERO,       // '0' 
    BINDIG,      // '1' 
    OCTDIG,      // '2'|'3'|'4'|'5'|'6'|'7' 
    DECDIG,      // '8'|'9' 
    HEXDIG,      // 'A' |'C'|'D' |'F' 
    PREHEX,      // 'x' 
    PREBIN,      // 'b' 
    EXPDEC,      // 'e' 
    EXPBIN,      // 'p' 
    DECPNT,      // '.' 
    SGNCHR,      // '+' | '-' 
    OTHER 
}; 

enum fsm_states { 
    START, 
    SIGN, 
    PREFIX, 
    DECPOINT, 
    DECFRAC, 
    BINBASE, 
    OCTBASE, 
    DECBASE, 
    HEXBASE, 
    BINFRAC, 
    OCTFRAC, 
    HEXFRAC, 
    EXPOMARK, 
    EXPOSIGN, 
    EXPONENT, 
    ERROR 
}; 

#ifdef DEBUG 
static const char *st2str[16] = { 
    "START", 
    "SIGN", 
    "PREFIX", 
    "DECPOINT", 
    "DECFRAC", 
    "BINBASE", 
    "OCTBASE", 
    "DECBASE", 
    "HEXBASE", 
    "BINFRAC", 
    "OCTFRAC", 
    "HEXFRAC", 
    "EXPOMARK", 
    "EXPOSIGN", 
    "EXPONENT", 
    "ERROR" 
}; 
#endif 
// transition-table 
static int fsm_table[16][11] = { 
    {PREFIX, DECBASE, DECBASE, DECBASE, HEXBASE, ERROR, HEXBASE, HEXBASE, ERROR, 
    DECPOINT, SIGN}, 
    {PREFIX, DECBASE, DECBASE, DECBASE, HEXBASE, ERROR, HEXBASE, 
    HEXBASE, ERROR, DECPOINT, ERROR}, 
    {OCTBASE, OCTBASE, OCTBASE, ERROR, ERROR, HEXBASE, BINBASE, ERROR, ERROR, 
    DECFRAC, 
    ERROR}, 
    {DECFRAC, DECFRAC, DECFRAC, DECFRAC, HEXFRAC, ERROR, HEXFRAC, HEXFRAC, 
    EXPOMARK, ERROR, ERROR}, 
    {DECFRAC, DECFRAC, DECFRAC, DECFRAC, ERROR, ERROR, ERROR, EXPOMARK, ERROR, 
    ERROR, ERROR}, 
    {BINBASE, BINBASE, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, EXPOMARK, 
    BINFRAC, ERROR}, 
    {OCTBASE, OCTBASE, OCTBASE, ERROR, ERROR, ERROR, ERROR, ERROR, EXPOMARK, 
    OCTFRAC, ERROR}, 
    {DECBASE, DECBASE, DECBASE, ERROR, ERROR, ERROR, ERROR, EXPOMARK, ERROR, 
    DECFRAC, ERROR}, 
    {HEXBASE, HEXBASE, HEXBASE, HEXBASE, HEXBASE, ERROR, ERROR, ERROR, EXPOMARK, 
    HEXFRAC, ERROR}, 
    {BINFRAC, BINFRAC, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, EXPOMARK, ERROR, 
    ERROR}, 
    {OCTFRAC, OCTFRAC, OCTFRAC, ERROR, ERROR, ERROR, ERROR, ERROR, EXPOMARK, 
    ERROR, ERROR}, 
    {HEXFRAC, HEXFRAC, HEXFRAC, HEXFRAC, HEXFRAC, ERROR, HEXFRAC, HEXFRAC, 
    EXPOMARK, ERROR, ERROR}, 
    {EXPONENT, EXPONENT, EXPONENT, EXPONENT, ERROR, ERROR, ERROR, ERROR, ERROR, 
    ERROR, EXPOSIGN}, 
    {EXPONENT, EXPONENT, EXPONENT, EXPONENT, ERROR, ERROR, ERROR, ERROR, ERROR, 
    ERROR, ERROR}, 
    {EXPONENT, EXPONENT, EXPONENT, EXPONENT, ERROR, ERROR, ERROR, ERROR, ERROR, 
    ERROR, ERROR}, 
    {ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR} 
}; 


#include <ctype.h> 
static int check_type(char c) 
{ 
    int type; 
    switch (tolower(c)) { 
    case '0': 
     type = ZERO; 
     break; 
    case '1': 
     type = BINDIG; 
     break; 
    case '2': 
    case '3': 
    case '4': 
    case '5': 
    case '6': 
    case '7': 
     type = OCTDIG; 
     break; 
    case '8': 
    case '9': 
     type = DECDIG; 
     break; 
    case 'a': 
    case 'c': 
    case 'd': 
    case 'f': 
     type = HEXDIG; 
     break; 
    case 'x': 
     type = PREHEX; 
     break; 
    case 'b': 
     type = PREBIN; 
     break; 
    case 'e': 
     type = EXPDEC; 
     break; 
    case 'p': 
     type = EXPBIN; 
     break; 
    case '.': 
     type = DECPNT; 
     break; 
    case '+': 
    case '-': 
     type = SGNCHR; 
     break; 
    default: 
     type = OTHER; 
     break; 
    }; 
    return type; 
} 

// some variables to hold the relevant parts of a real number 
static int main_sign = 1; 
static int expo_sign = 1; 

static double integral_part = 0.0; 
static double fractional_part = 0.0; 
static int hex_frac = 0; 
static int dec_frac = 0; 
static int oct_frac = 0; 
static int bin_frac = 0; 

static int exponent_part = 0; 
// flags with both a boolean and a numerical value 
// prob. not the best idea but simple 
#define DECIMAL_EXP 10 
#define BINARY_EXP 2 
static int exponent_binordec = 0; 

// assuming ASCII. Full map for more bases in the future. 
// Far future. 
static const char digit_map[] = { 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x00-0x07 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x08-0x0f 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x10-0x17 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x18-0x1f 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x20-0x27 
    -1, -1, 1, -1, -1, -1, -1, -1,  // 0x28-0x2f '+' = 0x2b, '-' = 0x2d 
    0, 1, 2, 3, 4, 5, 6, 7,  // 0x30-0x37 
    8, 9, -1, -1, -1, -1, -1, -1,  // 0x38-0x3F 
    -1, 10, 11, 12, 13, 14, 15, -1,  // 0x40-0x47 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x48-0x4f 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x50-0x57 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x58-0x5f 
    -1, 10, 11, 12, 13, 14, 15, -1,  // 0x60-0x67 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x68-0x6f 
    -1, -1, -1, -1, -1, -1, -1, -1,  // 0x70-0x77 
    -1, -1, -1, -1, -1, -1, -1, -1  // 0x78-0x7f 
}; 


static int fsm(char input, int *state) 
{ 
    int res = FSM_OK; 

    input = tolower(input); 
#ifdef DEBUG 
    printf("INPUT: %c [0x%2x] (%d) STATE: %-10s\n", input, input,digit_map[+input],st2str[*state]); 
#endif 
    switch (*state) { 
    case START: 
     fprintf(stderr, 
       "Something is messed up in quite some hefty way and form!\n"); 
     exit(EXIT_FAILURE); 
    case SIGN: 
     main_sign = digit_map[+input]; 
     break; 
    case PREFIX: 
     // not used 
     break; 
    case DECPOINT: 
     // not used 
     break; 
    case DECBASE: 
     integral_part *= 10.0; 
     integral_part += digit_map[+input]; 
     break; 
    case BINBASE: 
     integral_part *= 2.0; 
     integral_part += digit_map[+input]; 
     break; 
    case OCTBASE: 
     integral_part *= 8.0; 
     integral_part += digit_map[+input]; 
     break; 
    case HEXBASE: 
     if (input == 'x') { 
     break; 
     } 
     integral_part *= 16.0; 
     integral_part += digit_map[+input]; 
     break; 
    case BINFRAC: 
     if (input == '.') { 
     break; 
     } 
     fractional_part *= 2.0; 
     fractional_part += digit_map[+input]; 
     bin_frac++; 
     break; 
    case OCTFRAC: 
     if (input == '.') { 
     break; 
     } 
     fractional_part *= 8.0; 
     fractional_part += digit_map[+input]; 
     oct_frac++; 
     break; 
    case DECFRAC: 
     if (input == '.') { 
     break; 
     } 
     fractional_part *= 10.0; 
     fractional_part += digit_map[+input]; 
     dec_frac++; 
     break; 
    case HEXFRAC: 
     if (input == '.') { 
     break; 
     } 
     fractional_part *= 16.0; 
     fractional_part += digit_map[+input]; 
     hex_frac++; 
     break; 
    case EXPOMARK: 
     exponent_binordec = (input == 'e') ? DECIMAL_EXP : BINARY_EXP; 
     break; 
    case EXPOSIGN: 
     expo_sign = digit_map[+input]; 
     break; 
    case EXPONENT: 
     exponent_part *= 10; 
     exponent_part += digit_map[+input]; 
     break; 
    case ERROR: 
     fprintf(stderr, "ERROR state reached\n"); 
     exit(EXIT_FAILURE); 
     // "Run in circles, scream and shout!" 
     // Infantry Journal, Vol. 35, p. 396, United States Infantry Association, 1929 
     // Or was it Herman Wouk? The Caine Mutiny? No, that would be 1951 and too late. 
     break; 
    default: 
     fprintf(stderr, "Unexpected state with input %c\n", input); 
     *state = ERROR; 
     res = FSM_ERROR; 
     break; 
    } 
    return res; 
} 

static char *trim_both(char *s) 
{ 
    char *end,*p; 
    p = s; 
    while (isspace(*p)) { 
    p++; 
    } 
    if (*p == '\0') { 
    return p; 
    } 
    end = p + strlen(p) - 1; 
    while (end > p && isspace(*end)) { 
    end--; 
    } 
    *(end + 1) = '\0'; 
    return p; 
} 

static int str2dbl(char *s, double *d) 
{ 
    int cur_state = START; 
    int type; 
    int res; 

    s = trim_both(s); 

    if(*s == '\0'){ 
    // empty input, would an error be better? 
    *d = 0.0; 
    return FSM_OK; 
    } 

    if(*s == '0' && *(s+1) == '\0'){ 
    *d = 0.0; 
    return FSM_OK; 
    } 

    if (!strncasecmp(s, "-inf", 4)) { 
    *d = -INFINITY; 
    return FSM_OK; 
    } 
    if (!strncasecmp(s, "+inf", 4) || !strncasecmp(s, "inf", 3)) { 
    *d = INFINITY; 
    return FSM_OK; 
    } 
    if (!strncasecmp(s, "-nan", 4) || !strncasecmp(s, "+nan", 4) || !strncasecmp(s, "nan", 3)) { 
    *d = NAN; 
    return FSM_OK; 
    } 

    while (*s != '\0') { 
    type = check_type(*s); 
    if (type == OTHER) { 
     fprintf(stderr, "OTHER: %c", *s); 
     return FSM_ERROR; 
    } 

    cur_state = fsm_table[cur_state][type]; 
    res = fsm(*s, &cur_state); 
    if (res != FSM_OK) { 
     break; 
    } 
    s++; 
    } 

    printf("exp; %d, intp: %.20g, fracp: %.20g\n", exponent_part, integral_part, 
     fractional_part); 

    // The following will work with arbitray precision in production, use of 
    // a "double" is just for testing and not for real use. The error is 
    // larger than one ulp in way too many times 

    // the integral part needs no treatment, yet. 
    *d += integral_part; 

    // the fractional part needs to be shifted by the base to the right 
    if (hex_frac > 0) { 
    fractional_part /= pow(16, hex_frac); 
    } else if (dec_frac > 0) { 
    fractional_part /= pow(10, dec_frac); 
    } else if (oct_frac > 0) { 
    fractional_part /= pow(8, oct_frac); 
    } else if (bin_frac > 0) { 
    fractional_part /= pow(2, bin_frac); 
    } 
    printf("fracp: %.20g\n", fractional_part); 
    *d += fractional_part; 

    // (exponent_binordec != 0) also means that there is an exponent in the first place 
    if (exponent_binordec) { 
    if (expo_sign >= 0) { 
     *d *= pow(exponent_binordec, exponent_part); 
    } else { 
     *d /= pow(exponent_binordec, exponent_part); 
    } 
    } 
    *d *= main_sign; 
    return FSM_OK; 
} 

int main(int argc, char **argv) 
{ 
    int res; 
    char *input, *endptr; 
    double out, libc; 

    if (argc != 2) { 
    fprintf(stderr, "Usage: %s float\n", argv[0]); 
    exit(EXIT_FAILURE); 
    } 

    input = malloc(strlen(argv[1]) + 1); 
    if (input == NULL) { 
    fprintf(stderr, "Malloc failed to allocate a measly %zu bytes\n", 
      strlen(argv[1]) + 1); 
    exit(EXIT_FAILURE); 
    } 
    strcpy(input, argv[1]); 

    // all checks ommited! 
    libc = strtod(input, &endptr); 
    printf("\nINPUT: %s\t%g\t%s\n\n", input, libc, endptr); 
    out = 0.0; 

    res = str2dbl(input, &out); 

    printf 
     ("fsm returned %d and the result (if any) is: \n\tinp: %s\n\town: %.20g\n\tlibc: %.20g\n", 
     res, trim_both(input) ,out, libc); 
    free(input); 
    exit(EXIT_SUCCESS); 
} 
相關問題