Эквивалент с плавающей запятой strtol () в C
strtol
преобразует введенную строку str в длинное значение любой указанной базы от 2 до 36. strtof()
предлагает аналогичную функциональность, но не позволяет вам указывать базу. Есть ли другая функция, которая делает то же самое, что и strtof
, но позволяет выбрать базу?
strtof("101.101", null, 2);
И получить на выходе 5.625.
3 ответа:
Вы можете разобрать строку, чтобы разделить ее на
.
и преобразовать часть до и часть после в десятичную дробь. После этого вы можете создать поплавок из этой строки. Вот простая функция, которая выполняет эту задачу.Можно было бы сделать это более эффективным и менее грязным способом, но это всего лишь пример, чтобы показать вам идею, стоящую за этим. Вышесказанное хорошо работает для меня.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
.
Вычисления с FP могут нести накопленные ошибки округления и другие тонкости. Ниже просто вычисляется целая числовая часть и дробная часть в виде 2 целых чисел с основанием 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
для менее ограничительной функции.
Для сравнения, вот простая, прямолинейная версия
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; }
Несколько менее очевидный подход, который может быть численно более эффективным, состоит в следующем:
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.