2016-10-31 33 views
2

strtol將輸入的字符串str轉換爲任意指定的2至36的鹼基的長整型值。strtof()提供了類似的功能,但不允許指定基數。是否有另一個功能與strtof相同,但允許您選擇基地?相當於C中strtol()的浮點數

例如假設101.101作爲字符串輸入。我希望能夠做

strtof("101.101", null, 2); 

,並得到5.625.

+0

你能展示一個浮點值轉換的例子嗎? –

+0

您可以簡單地在'。'之前和之後轉換零件。使用strtol,然後使兩者浮動 –

+0

@ZachP這對小數點前的數字起作用,但數字之後的數字是什麼? –

回答

1

輸出可以解析字符串給它的.分裂和小數點後和部分之前轉換的部分。之後,您可以創建一個浮動字符串。這是一個簡單的功能,可以實現這一點。

float new_strtof(char* const ostr, char** endptr, unsigned char base) 
{ 
    char* str = (char*)malloc(strlen(ostr) + 1); 
    strcpy(str, ostr); 
    const char* dot = "."; 

    /* I do not validate any input here, nor do I do anything with endptr */  //Let's assume input of 101.1101, null, 2 (binary) 
    char *cbefore_the_dot = strtok(str, dot); //Will be 101 
    char *cafter_the_dot = strtok(NULL, dot); //Will be 0101 

    float f = (float)strtol (cbefore_the_dot, 0, base); //Base would be 2 = binary. This would be 101 in decimal which is 5 
    int i, sign = (str[0] == '-'? -1 : 1); 
    char n[2] = { 0 }; //will be just for a digit at a time 

    for(i = 0 ; cafter_the_dot[i] ; i++) //iterating the fraction string 
    { 
     n[0] = cafter_the_dot[i]; 
     f += strtol(n, 0, base) * pow(base, -(i + 1)) * sign; //converting the fraction part 
    } 

    free(str); 
    return f; 
} 

人們可以用更高效,更不髒的方式來管理這個,但這只是一個例子來向你展示這個背後的想法。以上這些對我來說很好。

不要忘了#include <math.h>並編譯-lm標誌。一個例子是gcc file.c -o file -lm

+0

101.1101應該是5.8125,但你會走出5.13,對吧?也許你可以更清楚一點,二進制分數的表示是什麼,以及如何從樣本中得到小數部分 –

+0

以及如何處理小數部分的前導0? –

+0

@KarstenKoop你是對的,會照顧小數部分。但是,對於處理領先的0,你的意思是什麼?它會在哪裏引起問題? –

0

爲了便於比較,這裏有atoi()簡單,直接的版本,接受一個任意基地使用(即不一定是10):

#include <ctype.h> 

int myatoi(const char *str, int b) 
{ 
    const char *p; 
    int ret = 0; 
    for(p = str; *p != '\0' && isspace(*p); p++) 
     ; 
    for(; *p != '\0' && isdigit(*p); p++) 
     ret = b * ret + (*p - '0'); 
    return ret; 
} 

(請注意,我已經離開了負數處理)

一旦你得到了,這是簡單的檢測小數點和處理數字到它的右邊還有:

double myatof(const char *str, int b) 
{ 
    const char *p; 
    double ret = 0; 
    for(p = str; *p != '\0' && isspace(*p); p++) 
     ; 
    for(; *p != '\0' && isdigit(*p); p++) 
     ret = b * ret + (*p - '0'); 

    if(*p == '.') 
     { 
     double fac = b; 
     for(p++; *p != '\0' && isdigit(*p); p++) 
      { 
      ret += (*p - '0')/fac; 
      fac *= b; 
      } 
     } 

    return ret; 
} 

一個slightl Ÿ不太明顯的方式,這可能會更好地表現數值是:

double myatof2(const char *str, int b) 
{ 
    const char *p; 
    long int n = 0; 
    double denom = 1; 
    for(p = str; *p != '\0' && isspace(*p); p++) 
     ; 
    for(; *p != '\0' && isdigit(*p); p++) 
     n = b * n + (*p - '0'); 

    if(*p == '.') 
     { 
     for(p++; *p != '\0' && isdigit(*p); p++) 
      { 
      n = b * n + (*p - '0'); 
      denom *= b; 
      } 
     } 

    return n/denom; 
} 

#include <stdio.h> 

int main() 
{ 
    printf("%d\n", myatoi("123", 10)); 
    printf("%d\n", myatoi("10101", 2)); 

    printf("%f\n", myatof("123.123", 10)); 
    printf("%f\n", myatof("101.101", 2)); 

    printf("%f\n", myatof2("123.123", 10)); 
    printf("%f\n", myatof2("101.101", 2)); 

    return 0; 
} 

預期它打印

123 
21 
123.123000 
5.625000 
123.123000 
5.625000 

測試了這些。

還要說明一點:這些功能不處理基地超過10

0

計算與FP可能招致累積舍入誤差等細微之處更大。下面簡單地計算整數部分和小數部分爲2個base-n整數,然後用最小FP計算得出答案。

代碼還需要應付負整數部分,並確保小數部分用相同的符號處理。

#include <ctype.h> 
#include <math.h> 
#include <stdlib.h> 

double CC_strtod(const char *s, char **endptr, int base) { 
    char *end; 
    if (endptr == NULL) endptr = &end; 
    long ipart = strtol(s, endptr, base); 
    if ((*endptr)[0] == '.') { 
    (*endptr)++; 
    char *fpart_start = *endptr; 
    // Insure `strtol()` is not fooled by a space, + or - 
    if (!isspace((unsigned char) *fpart_start) && 
     *fpart_start != '-' && *fpart_start != '+') { 
     long fpart = strtol(fpart_start, endptr, base); 
     if (ipart < 0) fpart = -fpart; 
     return fma(fpart, pow(base, fpart_start - *endptr), ipart); 
    } 
    } 
    return ipart; 
} 

int main() { 
    printf("%e\n", CC_strtod("101.101", NULL, 2)); 
} 

輸出

5.625000e+00 

上述被限制在這兩個部分不應該超過的long的範圍內。代碼可以使用更廣泛的類型,如intmax_t用於限制較少的功能。