• AVR Freaks

ftoa: Simple implementation

Author
matthiaswatkins
Super Member
  • Total Posts : 247
  • Reward points : 0
  • Joined: 2006/05/05 06:07:45
  • Location: Boston
  • Status: offline
2006/08/21 11:11:07 (permalink)
0

ftoa: Simple implementation

I've seen several implementations of ftoa on the C18 forum, but none here.  Also, the C18 implementations seem to be fairly general, which makes it difficult to optimize them.  I wrote a simple implementation that takes a 32-bit float and converts it to a string.  Useful for lightweight applications where you need fast conversion, have control over the input float range, and don't need a lot of precision.  (You can add precision by adding cases to the switch statement.)  Currently the algorithm runs in about 500Tcy.  Any optimization hints would be appreciated.


/**************************************************
*
*    ftoa - converts float to string
*
***************************************************
*
*    This is a simple implemetation with rigid
*    parameters:
*            - Buffer must be 8 chars long
*            - 3 digits precision max
*            - absolute range is -524,287 to 524,287
*            - resolution (epsilon) is 0.125 and
*              always rounds down
**************************************************/
void ftoa(float Value, char* Buffer)
{
    union
    {
        float f;
   
        struct
        {
            unsigned int    mantissa_lo : 16;
            unsigned int    mantissa_hi : 7;   
            unsigned int     exponent : 8;
            unsigned int     sign : 1;
        };
    } helper;
   
    unsigned long mantissa;
    signed char exponent;
    unsigned int int_part;
    char frac_part[3];
    int i, count = 0;
   
    helper.f = Value;
    //mantissa is LS 23 bits
    mantissa = helper.mantissa_lo;
    mantissa += ((unsigned long) helper.mantissa_hi << 16);
    //add the 24th bit to get 1.mmmm^eeee format
    mantissa += 0x00800000;
    //exponent is biased by 127
    exponent = (signed char) helper.exponent - 127;
   
    //too big to shove into 8 chars
    if (exponent > 18)
    {
        Buffer[0] = 'I';
        Buffer[1] = 'n';
        Buffer[2] = 'f';
        Buffer[3] = '\0';
        return;
    }
   
    //too small to resolve (resolution of 1/8)
    if (exponent < -3)
    {
        Buffer[0] = '0';
        Buffer[1] = '\0';
        return;
    }
   
    count = 0;
   
    //add negative sign (if applicable)
    if (helper.sign)
    {
        Buffer[0] = '-';
        count++;
    }
   
    //get the integer part
    int_part = mantissa >> (23 - exponent);   
    //convert to string
    itoa(int_part, &Buffer[count]);
   
    //find the end of the integer
    for (i = 0; i < 8; i++)
        if (Buffer[i] == '\0')
        {
            count = i;
            break;
        }       

    //not enough room in the buffer for the frac part   
    if (count > 5)
        return;
   
    //add the decimal point   
    Buffer[count++] = '.';
   
    //use switch to resolve the fractional part
    switch (0x7 & (mantissa  >> (20 - exponent)))
    {
        case 0:
            frac_part[0] = '0';
            frac_part[1] = '0';
            frac_part[2] = '0';
            break;
        case 1:
            frac_part[0] = '1';
            frac_part[1] = '2';
            frac_part[2] = '5';           
            break;
        case 2:
            frac_part[0] = '2';
            frac_part[1] = '5';
            frac_part[2] = '0';           
            break;
        case 3:
            frac_part[0] = '3';
            frac_part[1] = '7';
            frac_part[2] = '5';           
            break;
        case 4:
            frac_part[0] = '5';
            frac_part[1] = '0';
            frac_part[2] = '0';           
            break;
        case 5:
            frac_part[0] = '6';
            frac_part[1] = '2';
            frac_part[2] = '5';           
            break;
        case 6:
            frac_part[0] = '7';
            frac_part[1] = '5';
            frac_part[2] = '0';           
            break;
        case 7:
            frac_part[0] = '8';
            frac_part[1] = '7';
            frac_part[2] = '5';                   
            break;
    }
   
    //add the fractional part to the output string
    for (i = 0; i < 3; i++)
        if (count < 7)
            Buffer[count++] = frac_part[i];
   
    //make sure the output is terminated
    Buffer[count] = '\0';
}

-Matt
#1

7 Replies Related Threads

    jetcode
    Super Member
    • Total Posts : 886
    • Reward points : 0
    • Joined: 2004/06/19 09:45:59
    • Location: San Geronimo Valley
    • Status: offline
    RE: ftoa: Simple implementation 2006/08/21 11:16:52 (permalink)
    0

    nice work ... is this algorithm more efficient than using sprintf?
    #2
    matthiaswatkins
    Super Member
    • Total Posts : 247
    • Reward points : 0
    • Joined: 2006/05/05 06:07:45
    • Location: Boston
    • Status: offline
    RE: ftoa: Simple implementation 2006/08/21 11:45:16 (permalink)
    0
    It's 10-20 times more efficient than sprintf, depending on how precise the float is.  sprintf takes 6000-12000Tcy.

    -Matt
    #3
    ashg010
    New Member
    • Total Posts : 3
    • Reward points : 0
    • Joined: 2013/03/08 03:19:42
    • Location: 0
    • Status: offline
    Re: RE: ftoa: Simple implementation 2013/03/08 03:44:05 (permalink)
    0
    Hey Matt first of all thanks a lot for posting the above code. It works awesome.
    I used it to display values over a touchscreen. The only problem is
    decimals after 0.7 gets rounded off. eg: after 36.7 directly 37 is displayed , 36.8 and 36.9 are ignored. I guess the error is with this line : 
     //use switch to resolve the fractional part    
     switch (0x00000007 & (mantissa  >> (20 - exponent)))
    It would be great if you could help me fix this.
    Thanks :)
    #4
    1and0
    Access is Denied
    • Total Posts : 9989
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: RE: ftoa: Simple implementation 2013/03/08 04:11:58 (permalink)
    +2 (1)
    ashg010
    The only problem is decimals after 0.7 gets rounded off. eg: after 36.7 directly 37 is displayed , 36.8 and 36.9 are ignored. I guess the error is with this line : 
    //use switch to resolve the fractional part    
    switch (0x00000007 & (mantissa  >> (20 - exponent)))
    It would be great if you could help me fix this.
    Like OP said, add more cases to the switch statement for more precision:
      
        switch (0xF & (mantissa  >> (20 - exponent)))
        {
            case 0:
            // ...
            case 15:
            // ..
        }
     
    EDIT:  Might also have to change this
     
        //too small to resolve (resolution of 1/8)
        if (exponent < -3) 
     
    to
     
        //too small to resolve (resolution of 1/16)
        if (exponent < -4)
    post edited by 1and0 - 2013/03/08 04:28:17
    #5
    ashg010
    New Member
    • Total Posts : 3
    • Reward points : 0
    • Joined: 2013/03/08 03:19:42
    • Location: 0
    • Status: offline
    Re: RE: ftoa: Simple implementation 2013/03/08 05:04:42 (permalink)
    0
    Thanks a lot for your reply but the issue still is unsolved.
    When I make the above changes it just wont take anymore cases more than 7
    i.e. in the switch case of  the fractional part.
    If i edit switch(0x7...) to say switch(0x9...) and try to add more cases only o and 9 are displayed after the decimal point.
     
    Thanks anyways I guess I should come up with my own code and not just blindly use this one.
    #6
    1and0
    Access is Denied
    • Total Posts : 9989
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: RE: ftoa: Simple implementation 2013/03/08 05:25:04 (permalink)
    +1 (1)
    ashg010
    If i edit switch(0x7...) to say switch(0x9...) and try to add more cases only o and 9 are displayed after the decimal point.
    Thanks anyways I guess I should come up with my own code and not just blindly use this one.
    That number in switch() is a bit-mask, try 0xF as I've suggested and see.  Always try to understand the code you borrowed from others.
    #7
    ashg010
    New Member
    • Total Posts : 3
    • Reward points : 0
    • Joined: 2013/03/08 03:19:42
    • Location: 0
    • Status: offline
    Re: RE: ftoa: Simple implementation 2013/03/08 05:48:25 (permalink)
    0
    Thanks a lot I could not ask for more spoon feeding.
    Its almost working except after say for eg. 36.7 next no. displayed is 37.8 and not 36.8 .
    No issues I will look into it. Thanks again. Cheers
    #8
    Jump to:
    © 2019 APG vNext Commercial Version 4.5