Convert a float to a string
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:
- printf: 0, dtoa: 0
- printf: 42, dtoa: 42
- printf: 1234567.8901234, dtoa: 1234567.89012344996444
- printf: 1.8e-14, dtoa: 1.79999999999999e-14
- printf: 555555.55555556, dtoa: 555555.55555555550381
- printf: -8.8888888888889e+14, dtoa: -8.88888888888888e+14
- printf: 1.1111111111111e+23, dtoa: 1.11111111111111e+23
Solution 3
- Use the
log
-function to find out the magnitudem
of your number. If the magnitude is negative print"0."
and an appropriate amount of zeros. - Consecutively divide by
10^m
and cast the result to int to get the decimal digits.m--
for the next digit. - If you came accross
m==0
, don't forget to print the decimal point"."
. - Break off after a couple of digits. If
m>0
when you break of, don't forget to print"E"
anditoa(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.
SIVA
Updated on July 09, 2022Comments
-
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 convertsnum
to a string and returns it.ftoa(3.1415)
should return"3.1415"
. -
Andi Jay almost 12 yearsThere'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 almost 12 yearsAnother 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 almost 10 yearsJust 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 over 8 yearsI 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 over 7 yearsNegative infinity prints
inf
right? Shouldn't it check the sign bit and return-inf
if negative? -
MD XF over 6 yearsLink-only answers are not generally accepted on this site.
-
MD XF over 6 yearsHmm, if you're already using
string.h
functions, might as well dostrcpy(str,"nan");
instead of individually changing characters. Also, if you don't mind usingmath.h
(and compile with-lm
), you can doisnan(value)
instead of the hacky test. -
gregsmi over 4 yearsDoesn't work for numbers with zeroes after the decimal point (e.g. 1.05->"1.5")...
-
RufusVS over 4 yearsare precision and tolerance meant to be the same? precision is undefined, and tolerance is unused.
-
Andrew Thaddeus Martin over 3 yearsThis 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.