Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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".

like image 633
SIVA Avatar asked Feb 20 '10 17:02

SIVA


3 Answers

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
like image 150
androider Avatar answered Sep 17 '22 17:09

androider


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';
like image 23
Sophy Pal Avatar answered Sep 20 '22 17:09

Sophy Pal


You can use C++20 std::format or the {fmt} library, std::format is based on, to convert a floating-point number into a string, for example:

std::string s = std::format("{}", M_PI);

The advantage of this method compared to sprintf is that std::format gives you the shortest decimal representation with a round-trip guarantee.

like image 30
vitaut Avatar answered Sep 20 '22 17:09

vitaut