2016-10-31 9 views
2

strtol преобразует введенную строку str в длинное значение любой заданной базы от 2 до 36. strtof() предлагает аналогичную функциональность, но не позволяет указать базу. Есть ли другая функция, которая делает то же самое, что и strtof, но позволяет выбрать базу?Плавающая точка, эквивалентная strtol() в C

например Скажем, 101.101 вводится в виде строки. Я хочу быть в состоянии сделать

strtof("101.101", null, 2); 

и получить выход 5.625.

+0

Можете ли вы показать пример значения с плавающей запятой для преобразования? –

+0

Вы можете просто преобразовать деталь до и после '.' используя strtol, а затем делая float из обоих –

+0

@ZachP. Это работает для чисел до десятичной точки, но как насчет чисел после? –

ответ

1

Вы можете разобрать строку разбить его в . и конвертировать часть прежде, а часть после того, как в десятичной системе. Впоследствии вы можете создать float из этой строки. Вот простая функция, которая это выполняет.

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

Расчеты с ФП могут понести накопила ошибки округления и другие тонкости. Ниже просто вычисляется целое число и дробная часть в виде 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 для менее ограничительной функции.