floating point to ASCII conversion

Discussion in 'C Programming' started by Peter Sprenger, May 24, 2007.

  1. Hello,

    I want to efficient convert floating point numbers (IEEE754) into a
    string. I have no library routines that do the job (like sprintf etc.),
    because I work in an embedded environment.

    My actual algorithm uses multiplying with 10 to shift the fraction into
    an integer value and to aquire the used exponent. But the drawback is
    obvious: When I have very small numbers like 3.141E-300 I have to make
    300 time consuming floating point multiplies to convert this number.

    But, since I know the IEEE754 structure and have directly access to the
    exponent (of base 2) of a fp number, is there a faster method to convert
    fp numbers to ASCII?

    Regards

    Peter
     
    Peter Sprenger, May 24, 2007
    #1
    1. Advertisements

  2. Peter Sprenger

    Eric Sosman Guest

    If you have the frexp() function available, that might
    make a good starting point.
     
    Eric Sosman, May 24, 2007
    #2
    1. Advertisements

  3. Nope, no frexp() available either.
     
    Peter Sprenger, May 24, 2007
    #3
  4. Peter Sprenger

    Eric Sosman Guest

    I'd suggest writing your own frexp() work-alike, using
    your knowledge of the floating-point representation. The
    advantage of using Standard-like tools for the task is that
    you'll easily be able to move the code to other platforms;
    something like a float-to-string-without-sprintf operation
    seems of sufficiently wide applicability that you may well
    want it again. So: invest a little non-portable work to get
    yourself up to the frexp()-ish baseline, and write portable C
    from there upwards.

    Good luck!
     
    Eric Sosman, May 24, 2007
    #4
  5. I was a little bit quick. In fact I can write a frexp() myself, that
    separates exponent and mantissa. But then? I have a mantissa and an
    exponent of base 2. I have no right idea to transform it from here to
    an ascii string.

    Regards

    Peter
     
    Peter Sprenger, May 24, 2007
    #5
  6. If you have log, pow and floor then given a positive x

    double log10 = log(x)/log(10.0);
    int f = floor( log10);
    double y = x*pow( 10.0, -f);

    gets you y with 1<=y<10 and x = y * pow(10,f);

    Duncan
     
    Duncan Muirhead, May 24, 2007
    #6
  7. If x is positive
    int expon;
    double frac = frexp(x, &expon);
    int f = floor( expon*log_10_2);
    double y = x*pow( 10.0, -f);
    gets you y with 1<=y<10 and x = y*pow(10,f)
    here log_10_2 is log base 10 of 2, ie around 0.301029995663981143

    You can get the digits of f by eg iterating
    d = (int)y; y = 10.0*(y-d);
    Note that you're only doing this for the number of digits required.

    If you don't have pow (and maybe even if you do) it can be written
    fairly easily & efficiently since its second argument is an integer.
    Duncan
     
    Duncan Muirhead, May 24, 2007
    #7
  8. Oops! Sorry, that's not quite right. In fact f above could be one
    too small, and so we could have 10<=y<=100. I think the easiest thing
    too do is to check for y being at least10, and if so fix it up.
    Duncan
     
    Duncan Muirhead, May 24, 2007
    #8
  9. Peter Sprenger

    Eric Sosman Guest

    Peter Sprenger wrote On 05/24/07 10:46,:
    You wrote originally about the time eaten up by the
    very many multiplications by ten needed to scale a value
    like 3.141E-300 to a reasonable range. I'm suggesting
    that you use the exponent of two to figure out how many
    "decades" of scaling you need, and do them all in one
    multiplication. You could use a precomputed array with
    the exponents as indices, or multiply the two's exponent
    by log10(2) and do a little rounding and/or truncating
    to get the ten's exponent.

    x = m * 2**e
    = m * (10**log10(2))**e
    = m * 10**(log10(2)*e)
    = m * 10**f
    = m * 10**floor(f) * 10**(f - floor(f))
    = (m * 10**(f - floor(f))) * 10**floor(f)
     
    Eric Sosman, May 24, 2007
    #9
  10. Hello Eric,

    you are right,

    x = m * 2**e

    is correct. But if I have use log10 in some way, isn't it easier to
    directly get the exponent with directly log10(f) ? (f is my fp number)
    And then multiply f to get it in the range 1.0 <= f < 10.0 ?

    Your solution will not bring the answer on how many decimal places in
    the fraction f has. So I have to multiply it anyway in turns to get the
    decimal places after the comma.

    Peter
     
    Peter Sprenger, May 25, 2007
    #10
  11. Hello Duncan,

    you know the IEEE754 floating point format? The exponent says, where in
    the mantissa the decimal point is (or should I say the binary point :)
    ) and the fraction part begins.

    Your terms don't use the mantissa, so I think they are not correct. But
    like I wrote to Eric, if I can make a log10(x), I can bring x into
    1<= x < 10 with one multiplication.

    Peter
     
    Peter Sprenger, May 25, 2007
    #11
  12. Indeed, you want a log10 and a pow. However all you really need is the
    largest integer less than log10(x). Above I was musing about how you could
    do that if you had frexp.
    The point is that if
    y = frexp( x, &p)
    then we have x = pow(2,p)*y and 0.5<=y<=1
    Taking logs (base 10) of this
    log10(x) = p*log10(2) + log10(y)
    but -log10(2)<=log10(y) <= 0
    so the largest integer less than log10(x) is either the largest integer
    less than p*log10(2), or one less than this, You can figure out which
    by computing f = floor( p*log10) and then computing y = x*pow(10,-p).
    if 1<=y<10 we're done; otherwise (ie 0.1<=y<10) multiply y by 0.1
    and increase p by one.
    Duncan
     
    Duncan Muirhead, May 25, 2007
    #12
  13. Peter Sprenger

    Eric Sosman Guest

    You only need an approximation to log10(f) to determine
    the initial scaling amount. You can get that by extracting
    the binary exponent (which is close to lg(f)) and multiplying
    by the precomputed constant log10(2), then truncating or
    rounding to a nearby integer.
    Yes, but you avoid the nearly three hundred multiplications
    by ten that work through the leading zeroes of 3.141E-300.
    If you get to 0.1 <= abs(f)*scale < 1 in one step, you only
    need as many additional multiplications as you want digits.[*]

    [*] Optimization: multiply by 1E8 instead of by 10, and
    work on groups of eight digits at a time in `long' instead
    of one digit at a time in floating-point.
     
    Eric Sosman, May 25, 2007
    #13
  14. Peter Sprenger

    Thad Smith Guest

    Peter,

    Duncan has a good approach. It uses an frexp(), which you can write, a
    floating multiplication and conversion to integer, then the pow()
    function, which can be implemented with some combination of table
    lookups and multiplication. And, as Duncan noted later, you may need to
    do a final adjustment on the power of 10.

    In general, the larger the lookup table, the fewer multiplications that
    are needed. For example, there are 81 different powers of 10 from 1e-40
    to 1e+40. You can use a table of powers of 1e9 from 1e-36 to 1e+36,
    then powers of 10 from 1e-4 to 1e8. With 22 entries and one
    multiplication you get any power of 10 from 1e-40 to 1e+40. Or you can
    use a table with 81 entries and no multiplication. There are other
    feasible combinations, as well.
     
    Thad Smith, May 27, 2007
    #14
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.