2

strtol converts the inputed string str to a long value of any specified base of 2 to 36. strtof() offers a similar functionality but without allowing your to specify base. Is there another function that does the same as strtof but allows you to select base?

e.g Lets say 101.101 is inputted as a string. I want to be able to do

strtof("101.101", null, 2);

and get an output of 5.625.

3 Answers3

1

You can parse the string to split it in the . and convert the part before and the part after to decimal. Afterwards, you can create a float out of that string. Here is a simple function that accomplishes that.

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;
}

One could manage this in a more efficient and less dirty way but this is just an example to show you the idea behind this. The above works well for me.

Don't forget to #include <math.h> and compile with -lm flag. An example would be gcc file.c -o file -lm.

Zach P
  • 1,731
  • 11
  • 16
0

For comparison, here's a simple, straightforward version of atoi(), that accepts an arbitrary base to use (i.e. not necessarily 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;
}

(Note that I have left out negative number handling.)

Once you've got that, it's straightforward to detect a decimal point and handle digits to the right of it as well:

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;
}

A slightly less obvious approach, which might be numerically better behaved, is:

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;
}

I tested these with

#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;
}

which prints

123
21
123.123000
5.625000
123.123000
5.625000

as expected.

One more note: these functions don't handle bases greater than 10.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

Calculations with FP can incur accumulated round off errors and other subtleties. The below simply calculates the whole number part and the fractional part as 2 base-n integers and then, with minimally FP calculation, derives the answer.

Code also needs to cope with a negative whole number part and insure the fractional part is treated with the same sign.

#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));
}

Output

5.625000e+00

The above is limited in that the two parts should not exceed the range of long. Code could use wider types like intmax_t for a less restrictive function.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256