Convert double/float to string
I need to convert a floating point number to an equivalent string in decimal (or other base). Conversion at first needs to be done in the format xE+0
where x
is the floating point number.
The idea I have is to first truncate the floating point number into a temporary integer and then convert that integer into string, and then consider the fractional part, multiply it with 10
while the fractional part does not become 0
. After the fractional part is transferred into the left side of the decimal point, apply the integer to string function again and convert the fraction part to string. Is there a better way, which will be faster than this? Will this method induce any kind of side effects?
To convert the floating point number into exponential representation shall I do the same as above and then adjust the power? Or directly bitmask the IEEE 754 floating point representation and convert each part into string.
Note: No other functions could be used, because I have access to absolutely no library functions. This cod开发者_JAVA技巧e goes into a toy kernel.
Use snprintf()
from stdlib.h
. Worked for me.
double num = 123412341234.123456789;
char output[50];
snprintf(output, 50, "%f", num);
printf("%s", output);
The only exact solution is to perform arbitrary-precision decimal arithmetic for the base conversion, since the exact value can be very long - for 80-bit long double
, up to about 10000 decimal places. Fortunately it's "only" up to about 700 places or so for IEEE double
.
Rather than working with individual decimal digits, it's helpful to instead work base-1-billion (the highest power of 10 that fits in a 32-bit integer) and then convert these "base-1-billion digits" to 9 decimal digits each at the end of your computation.
I have a very dense (rather hard to read) but efficient implementation here, under LGPL MIT license:
http://git.musl-libc.org/cgit/musl/blob/src/stdio/vfprintf.c?h=v1.1.6
If you strip out all the hex float support, infinity/nan support, %g
/%f
/%e
variation support, rounding (which will never be needed if you only want exact answers), and other things you might not need, the remaining code is rather simple.
Go and look at the printf()
implementation with "%f"
in some C library.
I know maybe it is unnecessary, but I made a function which converts float to string:
CODE:
#include <stdio.h>
/** Number on countu **/
int n_tu(int number, int count)
{
int result = 1;
while(count-- > 0)
result *= number;
return result;
}
/*** Convert float to string ***/
void float_to_string(float f, char r[])
{
long long int length, length2, i, number, position, sign;
float number2;
sign = -1; // -1 == positive number
if (f < 0)
{
sign = '-';
f *= -1;
}
number2 = f;
number = f;
length = 0; // Size of decimal part
length2 = 0; // Size of tenth
/* Calculate length2 tenth part */
while( (number2 - (float)number) != 0.0 && !((number2 - (float)number) < 0.0) )
{
number2 = f * (n_tu(10.0, length2 + 1));
number = number2;
length2++;
}
/* Calculate length decimal part */
for (length = (f > 1) ? 0 : 1; f > 1; length++)
f /= 10;
position = length;
length = length + 1 + length2;
number = number2;
if (sign == '-')
{
length++;
position++;
}
for (i = length; i >= 0 ; i--)
{
if (i == (length))
r[i] = '\0';
else if(i == (position))
r[i] = '.';
else if(sign == '-' && i == 0)
r[i] = '-';
else
{
r[i] = (number % 10) + '0';
number /=10;
}
}
}
See if the BSD C Standard Library has fcvt(). You could start with the source for it that rather than writing your code from scratch. The UNIX 98 standard fcvt() apparently does not output scientific notation so you would have to implement it yourself, but I don't think it would be hard.
Use this:
void double_to_char(double f,char * buffer){
gcvt(f,10,buffer);
}
Alternatively, you could use C99 output format which is: [-]0x1.<significand>p<biasied_exponent-bias>
, bias is 0x3ff
for double precision numbers. This is also preferred format for serialization as it's exact and would not depend on current settings of floating point environment (like rounding or flushing denormals to zero). A bit usual, but may be still useful and very fast.
sprintf can do this:
#include <stdio.h>
int main() {
float w = 234.567;
char x[__SIZEOF_FLOAT__];
sprintf(x, "%g", w);
puts(x);
}
精彩评论