浮点数相当于C中的strtol()

时间:2016-10-31 07:17:05

标签: c binary base strtol

strtol将输入的字符串str转换为2到36的任何指定基数的long值。strtof()提供了类似的功能,但不允许您指定base。是否有其他功能与strtof相同但允许您选择基础?

例如,假设101.101作为字符串输入。我希望能够做到

strtof("101.101", null, 2);

并获得5.625.

的输出

3 个答案:

答案 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,以减少限制性功能。