C中与strtol()等价的浮点

C中与strtol()等价的浮点,c,binary,base,strtol,C,Binary,Base,Strtol,strtol将输入的字符串str转换为任意指定基数为2到36的长值。strof提供了类似的功能,但不允许用户指定基。是否还有其他功能与strof相同,但允许您选择base e、 假设101.101作为字符串输入。我希望能够做到 strtof("101.101", null, 2); 并得到一个5.625的输出。您可以解析该字符串以在中拆分它。并将前面和后面的部分转换为十进制。之后,可以使用该字符串创建一个浮点。这里有一个简单的函数来实现这一点 float new_strtof(char* co

strtol将输入的字符串str转换为任意指定基数为2到36的长值。strof提供了类似的功能,但不允许用户指定基。是否还有其他功能与strof相同,但允许您选择base

e、 假设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;
}
你可以用一种更有效、更不肮脏的方式来管理它,但这只是一个例子,向你展示了背后的想法。以上对我来说很好


不要忘记使用-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 != '\0' && isspace(*p); p++)
        ;
    for(; *p != '\0' && isdigit(*p); p++)
        ret = b * ret + (*p - '0');
    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;
}
正如所料


还有一点需要注意:这些函数不处理大于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));
}

上述限制是因为这两个部分不应超过长的范围。代码可以使用更广泛的类型,如intmax\u t,以实现限制性更小的函数。

您可以展示一个要转换的浮点值的示例吗?您只需使用strtol转换“.”前后的部分,然后使用both@ZachP这适用于小数点之前的数字,但小数点之后的数字呢?@christorclarke我在回答中写了这个,它对我有用101.1101应该是5.8125,但你会得到5.13,对吗?也许你可以再弄清楚一点,二进制分数的表示法是什么,你是如何在你的样本中从中得到小数的,你会如何处理小数部分的前导0?@KarstenKoop你是对的,会处理小数部分。不过,你说的处理前导0是什么意思?在哪里会导致问题?数字1.1和1.01不应导致相同的问题result@KarstenKoop事实上,这是分数计算中的一个问题,现在可以解决了
123
21
123.123000
5.625000
123.123000
5.625000
#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