strtol
将输入的字符串str转换为2到36的任何指定基数的long值。strtof()
提供了类似的功能,但不允许您指定base。是否有其他功能与strtof
相同但允许您选择基础?
例如,假设101.101作为字符串输入。我希望能够做到
strtof("101.101", null, 2);
并获得5.625.
答案 0 :(得分: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
。
答案 1 :(得分: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;
}
一种稍微不那么明显的方法,可能在数值上表现得更好,是:
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的基数。
答案 2 :(得分: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
,以减少限制性功能。