相当于 C 中的 strtol() 的浮点数
Floating point equivalent to strtol() in C
strtol
将输入的字符串 str 转换为 2 到 36 之间任何指定基数的长整型值。strtof()
提供类似的功能,但不允许您指定基数。是否有另一个功能与 strtof
相同但允许您 select base?
例如假设 101.101 作为字符串输入。我希望能够做到
strtof("101.101", null, 2);
并得到 5.625.
的输出
您可以解析字符串以在.
中将其拆分并将前后部分转换为十进制。之后,您可以从该字符串创建一个浮点数。这是一个完成该操作的简单函数。
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
.
为了比较,这里有一个简单直接的 atoi()
版本,它接受任意基数来使用(即不一定是 10):
#include <ctype.h>
int myatoi(const char *str, int b)
{
const char *p;
int ret = 0;
for(p = str; *p != '[=10=]' && isspace(*p); p++)
;
for(; *p != '[=10=]' && 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 != '[=11=]' && isspace(*p); p++)
;
for(; *p != '[=11=]' && isdigit(*p); p++)
ret = b * ret + (*p - '0');
if(*p == '.')
{
double fac = b;
for(p++; *p != '[=11=]' && 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 != '[=12=]' && isspace(*p); p++)
;
for(; *p != '[=12=]' && isdigit(*p); p++)
n = b * n + (*p - '0');
if(*p == '.')
{
for(p++; *p != '[=12=]' && 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 的基数。
使用 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
来实现限制较少的功能。
strtol
将输入的字符串 str 转换为 2 到 36 之间任何指定基数的长整型值。strtof()
提供类似的功能,但不允许您指定基数。是否有另一个功能与 strtof
相同但允许您 select base?
例如假设 101.101 作为字符串输入。我希望能够做到
strtof("101.101", null, 2);
并得到 5.625.
您可以解析字符串以在.
中将其拆分并将前后部分转换为十进制。之后,您可以从该字符串创建一个浮点数。这是一个完成该操作的简单函数。
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
.
为了比较,这里有一个简单直接的 atoi()
版本,它接受任意基数来使用(即不一定是 10):
#include <ctype.h>
int myatoi(const char *str, int b)
{
const char *p;
int ret = 0;
for(p = str; *p != '[=10=]' && isspace(*p); p++)
;
for(; *p != '[=10=]' && 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 != '[=11=]' && isspace(*p); p++)
;
for(; *p != '[=11=]' && isdigit(*p); p++)
ret = b * ret + (*p - '0');
if(*p == '.')
{
double fac = b;
for(p++; *p != '[=11=]' && 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 != '[=12=]' && isspace(*p); p++)
;
for(; *p != '[=12=]' && isdigit(*p); p++)
n = b * n + (*p - '0');
if(*p == '.')
{
for(p++; *p != '[=12=]' && 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 的基数。
使用 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
来实现限制较少的功能。