Convert a float to a string

73,610

Solution 1

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 2

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

Solution 4

 /*
  * Program to convert float number to string without using sprintf
  */

#include "iostream"    
#include "string"    
#include "math.h"

# define PRECISION 5

using namespace std;

char*  floatToString(float num)
{
   int whole_part = num;
   int digit = 0, reminder =0;
   int log_value = log10(num), index = log_value;
   long wt =0;

   // String containg result
   char* str = new char[20];

   //Initilise stirng to zero
   memset(str, 0 ,20);

   //Extract the whole part from float num
   for(int  i = 1 ; i < log_value + 2 ; i++)
   {
       wt  =  pow(10.0,i);
       reminder = whole_part  %  wt;
       digit = (reminder - digit) / (wt/10);

       //Store digit in string
       str[index--] = digit + 48;              // ASCII value of digit  = digit + 48
       if (index == -1)
          break;    
   }

    index = log_value + 1;
    str[index] = '.';

   float fraction_part  = num - whole_part;
   float tmp1 = fraction_part,  tmp =0;

   //Extract the fraction part from  num
   for( int i= 1; i < PRECISION; i++)
   {
      wt =10; 
      tmp  = tmp1 * wt;
      digit = tmp;

      //Store digit in string
      str[++index] = digit +48;           // ASCII value of digit  = digit + 48
      tmp1 = tmp - digit;
   }    

   return str;
}


//Main program
void main()
{
    int i;
    float f = 123456.789;
    char* str =  floatToString(f);
    cout  << endl <<  str;
    cin >> i;
    delete [] str;
}

Solution 5

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.

Share:
73,610
SIVA
Author by

SIVA

Updated on July 09, 2022

Comments

  • SIVA
    SIVA almost 2 years

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

  • Andi Jay
    Andi Jay almost 12 years
    There's a problem with this. If num is smaller than 1 you will calculate the string wrong. You need to change the 3rd line to be floor(log10(num))
  • Andi Jay
    Andi Jay almost 12 years
    Another issue is if you try to convert a number that has a 0 in the ones place. For example 230.0. You will only get 23 in this case the way it is written now. You have to change the condition in the while loop to ((num > 0 + precision)||(m >= 0))
  • Toby
    Toby almost 10 years
    Just to note that this implementation, and androider's improvement, is good. Hpwever, with a high precision (eg: 9 dp) the repeated pow() can make this rather slow. Not great if this is part of other actions providing user feedback I've found.
  • CodeMouse92
    CodeMouse92 over 8 years
    I snagged this one for my (open source) library. Commenting everything and making it slightly more efficient. Once I'm done, would you rather I A) edit your answer with the tweaked code, or B) posted it separately and credited you with the original? I'm good either way. (If I don't hear, I'll assume B).
  • doug65536
    doug65536 over 7 years
    Negative infinity prints inf right? Shouldn't it check the sign bit and return -inf if negative?
  • MD XF
    MD XF over 6 years
    Link-only answers are not generally accepted on this site.
  • MD XF
    MD XF over 6 years
    Hmm, if you're already using string.h functions, might as well do strcpy(str,"nan"); instead of individually changing characters. Also, if you don't mind using math.h (and compile with -lm), you can do isnan(value) instead of the hacky test.
  • gregsmi
    gregsmi over 4 years
    Doesn't work for numbers with zeroes after the decimal point (e.g. 1.05->"1.5")...
  • RufusVS
    RufusVS over 4 years
    are precision and tolerance meant to be the same? precision is undefined, and tolerance is unused.
  • Andrew Thaddeus Martin
    Andrew Thaddeus Martin over 3 years
    This function turns encodes the number 1e-9 as :e-10, which is incorrect. Found this when porting this solution to another language. I'm not sure what the best fix is.