Convert a float to a string

How can I convert a floating point integer to a string in C/C++ without the library function sprintf?

I'm looking for a function, e.g. char *ftoa(float num) that converts num to a string and returns it.

ftoa(3.1415) should return "3.1415".


Solution 1:

Based on Sophy Pal's answer, this is a slightly more complete solution that takes into account the number zero, NaN, infinite, negative numbers, and scientific notation. Albeit sprintf still provides a more accurate string representation.

/* 
   Double to ASCII Conversion without sprintf.
   Roughly equivalent to: sprintf(s, "%.14g", n);
*/

#include <math.h>
#include <string.h>
// For printf
#include <stdio.h>

static double PRECISION = 0.00000000000001;
static int MAX_NUMBER_STRING_SIZE = 32;

/**
 * Double to ASCII
 */
char * dtoa(char *s, double n) {
    // handle special cases
    if (isnan(n)) {
        strcpy(s, "nan");
    } else if (isinf(n)) {
        strcpy(s, "inf");
    } else if (n == 0.0) {
        strcpy(s, "0");
    } else {
        int digit, m, m1;
        char *c = s;
        int neg = (n < 0);
        if (neg)
            n = -n;
        // calculate magnitude
        m = log10(n);
        int useExp = (m >= 14 || (neg && m >= 9) || m <= -9);
        if (neg)
            *(c++) = '-';
        // set up for scientific notation
        if (useExp) {
            if (m < 0)
               m -= 1.0;
            n = n / pow(10.0, m);
            m1 = m;
            m = 0;
        }
        if (m < 1.0) {
            m = 0;
        }
        // convert the number
        while (n > PRECISION || m >= 0) {
            double weight = pow(10.0, m);
            if (weight > 0 && !isinf(weight)) {
                digit = floor(n / weight);
                n -= (digit * weight);
                *(c++) = '0' + digit;
            }
            if (m == 0 && n > 0)
                *(c++) = '.';
            m--;
        }
        if (useExp) {
            // convert the exponent
            int i, j;
            *(c++) = 'e';
            if (m1 > 0) {
                *(c++) = '+';
            } else {
                *(c++) = '-';
                m1 = -m1;
            }
            m = 0;
            while (m1 > 0) {
                *(c++) = '0' + m1 % 10;
                m1 /= 10;
                m++;
            }
            c -= m;
            for (i = 0, j = m-1; i<j; i++, j--) {
                // swap without temporary
                c[i] ^= c[j];
                c[j] ^= c[i];
                c[i] ^= c[j];
            }
            c += m;
        }
        *(c) = '\0';
    }
    return s;
}

int main(int argc, char** argv) {

    int i;
    char s[MAX_NUMBER_STRING_SIZE];
    double d[] = {
        0.0,
        42.0,
        1234567.89012345,
        0.000000000000018,
        555555.55555555555555555,
        -888888888888888.8888888,
        111111111111111111111111.2222222222
    };
    for (i = 0; i < 7; i++) {
        printf("%d: printf: %.14g, dtoa: %s\n", i+1, d[i], dtoa(s, d[i]));
    }
}

Outputs:

  1. printf: 0, dtoa: 0
  2. printf: 42, dtoa: 42
  3. printf: 1234567.8901234, dtoa: 1234567.89012344996444
  4. printf: 1.8e-14, dtoa: 1.79999999999999e-14
  5. printf: 555555.55555556, dtoa: 555555.55555555550381
  6. printf: -8.8888888888889e+14, dtoa: -8.88888888888888e+14
  7. printf: 1.1111111111111e+23, dtoa: 1.11111111111111e+23

Solution 2:

When you're dealing with fp numbers, it can get very compex but the algorithm is simplistic and similar to edgar holleis's answer; kudos! Its complex because when you're dealing with floating point numbers, the calculations will be a little off depending on the precision you've chosen. That's why its not good programming practice to compare a float to a zero.

But there is an answer and this is my attempt at implementing it. Here I've used a tolerance value so you don't end up calculating too many decimal places resulting in an infinite loop. I'm sure there might be better solutions out there but this should help give you a good understanding of how to do it.

char fstr[80];
float num = 2.55f;
int m = log10(num);
int digit;
float tolerance = .0001f;

while (num > 0 + precision)
{
    float weight = pow(10.0f, m);
    digit = floor(num / weight);
    num -= (digit*weight);
    *(fstr++)= '0' + digit;
    if (m == 0)
        *(fstr++) = '.';
    m--;
}
*(fstr) = '\0';

Solution 3:

  1. Use the log-function to find out the magnitude m of your number. If the magnitude is negative print "0." and an appropriate amount of zeros.
  2. Consecutively divide by 10^m and cast the result to int to get the decimal digits. m-- for the next digit.
  3. If you came accross m==0, don't forget to print the decimal point ".".
  4. Break off after a couple of digits. If m>0 when you break of, don't forget to print "E" and itoa(m).

Instead of the log-function you can also directly extract the exponent by bitshifting and correcting for the exponent's offset (see IEEE 754). Java has a double-to-bits function to get at the binary representation.