33

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 code goes into a toy kernel.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
phoxis
  • 60,131
  • 14
  • 81
  • 117
  • 12
    I pray that people will just try to answer this question instead of asking "Why can't you use library functions?". – Frerich Raabe Aug 29 '11 at 09:47
  • need to implement a basic library – phoxis Aug 29 '11 at 09:48
  • 3
    http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ might be helpful as a starting point – user786653 Aug 29 '11 at 09:49
  • The whole mantissa is the "fractional part". An IEEE 754 normalized floating-point number **is** a fractional part (the "1." is implicit). – Pascal Cuoq Aug 29 '11 at 10:24
  • The method you're considering will throw away precision and have bad rounding errors. How much precision do you need though? A fixed number of places? Enough to reproduce the exact value of the `double` when the decimal representation is read back in? The exact value? – R.. GitHub STOP HELPING ICE Aug 29 '11 at 13:58
  • @R..: The more precision the better. The exact value is ideal. It would be better if the algorithm is able to preserve the original precision, or as close as possible. Can you point me to any other approach, where precision will be better preserved? – phoxis Aug 29 '11 at 14:59
  • Are you sure that `x+E0` is the format you want? If so, what does it mean? Or did you mean `xE+0`? – Keith Thompson Aug 29 '11 at 15:18
  • @Keith Thompson: right, sorry i want `xE+0` actually , corrected in the question. – phoxis Aug 29 '11 at 16:02
  • Possible duplicate of [Algorithm to convert an IEEE 754 double to a string?](https://stackoverflow.com/questions/7153979/algorithm-to-convert-an-ieee-754-double-to-a-string) – phuclv May 25 '17 at 15:51

9 Answers9

30

Use snprintf() from stdlib.h. Worked for me.

double num = 123412341234.123456789; 
char output[50];

snprintf(output, 50, "%f", num);

printf("%s", output);
nbro
  • 15,395
  • 32
  • 113
  • 196
sujeeth.mr
  • 333
  • 3
  • 2
  • 11
    i can't use any standard library functions, because the platform i am working nothing is available. – phoxis Mar 23 '12 at 13:20
  • 2
    I compiled this code with the GCC and with the LLVM/Clang - a result is "123412341234.123459", but not "123412341234.123456789". This answer partially correct since it working only for a small range float values. – PADYMKO Feb 21 '17 at 09:57
  • 1
    @PADYMKO This is not a fault of the code, but a result of floating-point numbers only being able to hold ~15 digits in decimal. For this example, just "123412341234.123459" already converts to the same double value. You can [see the next and previous doubles](https://gcc.godbolt.org/z/37dv9T). – Mingye Wang Sep 21 '20 at 21:21
22

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Are you saying me to convert first to 2^32 base number and then from it get 10 base string ? A bit of comment in the code and/or a brief description would really help to get into the code. – phoxis Aug 29 '11 at 16:45
  • 1
    @phoxis: You're interested in `fmt_fp`. `y` is the number that is outputted to `f`. `w` = string width, `p` = precision, `fl` = flags, `t` = type. the format code `"%20.5g` gives `w=20, p=5, fl=0, t='g'`. (`w=0, p=-1` gives you default formatting). It should be feasible to extract the parts you need. (Note I've only briefly looked at the source code, but it looks fairly solid and works correctly for the cases the quick-and-dirty one I linked to fails on) – user786653 Aug 29 '11 at 17:04
  • @phoxis: Something to get you started: http://pastebin.com/c84Dbvd7 (Apologies in advance to R.. for any confusion I've added to the discussion) – user786653 Aug 29 '11 at 18:03
  • 1
    Seeing how links can go down (and not everyone can always access them), maybe you could add the relevant, _"remaining code"_ directly to your answer? – domsson Mar 03 '20 at 17:53
7

Go and look at the printf() implementation with "%f" in some C library.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
wormsparty
  • 2,481
  • 19
  • 31
5

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;
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JJJakubJJ
  • 67
  • 1
  • 1
  • 2
    This may at best print a few small floating-point numbers correctly. A correct answer for all double-precision numbers has already been posted by R.. in 2011, what is the value of adding an incorrect one now? – Pascal Cuoq Jun 18 '14 at 07:46
  • I was only help and maybe show way how convert float into string. I try string convert back with atof and it worked. – JJJakubJJ Jun 18 '14 at 07:55
4

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.

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
Mike Crawford
  • 2,232
  • 2
  • 18
  • 28
2

Use this:

void double_to_char(double f,char * buffer){
    gcvt(f,10,buffer);
}
Ingo
  • 5,239
  • 1
  • 30
  • 24
0

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.

Kovalex
  • 1,720
  • 2
  • 11
  • 8
0

I'll interpret your question as "convert double/float to string" and not get too much into the exact suggested format; I assume you want something that "represents the value as good as possible in ASCII" and preferably can be round-tripped back to the same floating point value.

There are a few options available:

  • Ryan Juckett does a fairly good job at describing his (Dragon41) implementation in this 4-part blog post: https://www.ryanjuckett.com/printing-floating-point-numbers. Note: his implementation is C++; if C is a strict requirement for your use case, this is out of the story.
  • dtoa.c from netlib.org, referred to from Ryan's blog post:

    Written by David Gay (author of one of the above papers), this seems to be the accepted safe implementation. It has been iterated on since 1991 and is used in everything from Python to MySQL to the Nintendo Wii.

  • double-conversion, also referred to in Ryan's blog posts - the implementation in use in the Google V8 JS engine. Uses the Grisu2 algorithm. This is again in C++, so may or may not be an option.
  • Milo Yip's implementation from RapidJSON. This uses some C++ constructs, but a quick glance indicates that this can probably quite easily be converted to C if needed.

1: https://kurtstephens.com/files/p372-steele.pdf

2: https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
-4

sprintf can do this:

#include <stdio.h>
int main() {
  float w = 234.567;
  char x[__SIZEOF_FLOAT__];
  sprintf(x, "%g", w);
  puts(x);
}
  • 3
    This has a problem. __SIZEOF_FLOAT__ is the number of bytes in memory a float uses. Here you need the number of characters needed to represent any float. – Tod Aug 02 '17 at 04:15