Page 1 of 1

double/long double printing code

Posted: Tue Jul 07, 2009 10:57 pm
by 01000101
hey, as one of the functions of this forum is to have code testing, I'd like to present some code that I think works great, but could use some extra refining and case handling. This code prints a double-precision floating-point number using some fairly standard functions. This code is easily modifiable to print extended-precision numbers (long double) just by changing every reference of "double" to "long double". This code has been tested in my 32-bit x86 port and 64-bit x86-64 port of my test OS called ArcticOS. The only real kernel-level handling required is the FPU to be initialised with a control word, FINIT to be executed (read the wiki entry), and some sort of FPU-state saving (in multi-tasking kernels). 99% of this is guess-work and I'm sure could be done more efficiently, that's where you guys come in :wink: .

Code: Select all

// floating-point printf() support.  Usage: %xf/g.  Example: %4f (prints a double float to 4 (max) decimal places)
#define MAX_PRECISION    50

#define IsNaN(n) (n != n)

// %f: double/single precision support (double or promoted float, 64-bits)
#if defined(X86_COMMON)
static void print_double_float(double val, size_t precision)
{
    size_t cur_prec = 1;

    // if the user-defined precision is out-of-bounds, normalize it
    if(precision > MAX_PRECISION)
        precision = MAX_PRECISION;

    // if it's negative, show it!
    if(val < 0)
    {
        putch('-');
        
        // change to a positive value
        val = -val;
    }

    // check to see if it is Not-a-Number
    if(IsNaN(val))
    {
        putstr("NaN");
        return;
    }
    
    // print the integer part of the floating point
    print_int((int)val);
    
    // if precision == 0, only print the integer part 
    if(!precision)
        return;
    
    // now on to the decimal potion
    putch('.');
    
    // remove the integer part
    val -= (double)((int)val);
    
    /* on every iteration, make sure there are still decimal places left that are non-zero, 
       and make sure we're still within the user-defined precision range. */
    while(val > (double)((int)val) && cur_prec++ < precision+1)
    {
        // move the next decimal into the integer portion and print it
        val *= 10;
        print_int((int)val);
        
        /* if the value is == the floored value (integer portion), 
           then there are no more decimal places that are non-zero. */
        if(val == (double)((int)val))
            return;
        
        // subtract the integer portion
        val -= (double)((int)val);
    }
}
#endif
This will print up to "precision" digits, less if there are no more digits to print (if the double value is equal to its integer counterpart). I haven't added testing for +/- infinities. Also, the NaN testing is extremely primitive. This code also works when using SSE during compile-time. I release this under the public domain or whatever your country considers to be completely free for any purpose.

Re: double/long double printing code

Posted: Wed Jul 08, 2009 1:23 am
by Combuster

Code: Select all

    // print the integer part of the floating point
    print_int((int)val);
What about huge values (overflow)

Re: double/long double printing code

Posted: Wed Jul 08, 2009 3:45 am
by Creature
I never actually thought about printing floats or double's yet. If I was going to implement it, I would probably be doing something similar (multiplying by 10 to transform a floating point number into a normal integral number). I used something similar before to generate random floating point numbers.

Re: double/long double printing code

Posted: Wed Jul 08, 2009 4:17 am
by Solar
01000101 wrote:I haven't added testing for +/- infinities. Also, the NaN testing is extremely primitive.
A C99-compliant implementation of a standard lib should provide the functions isnan() and isinf() in <math.h>. I know that's of little help when you're in kernel space, I just wanted to mention it.
I release this under the public domain...
Excellent. Saved for later review when the time comes to add float / double printing to PDCLib. ;-)

Re: double/long double printing code

Posted: Wed Jul 08, 2009 1:05 pm
by 01000101
Combuster wrote:

Code: Select all

    // print the integer part of the floating point
    print_int((int)val);
What about huge values (overflow)
excellent observation, I do not handle integers > sizeof(size_t) bits wide. I'll probably just do the opposite of what I do to print the decimal (keep dividing by decrementing large multiples of 10) to print large doubles/long doubles.

Solar: Thanks, I'll look for those.