• AVR Freaks

Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Types

Author
vwheeler
Junior Member
  • Total Posts : 21
  • Reward points : 0
  • Joined: 2011/12/21 13:51:46
  • Location: 0
  • Status: offline
2012/10/26 11:12:38 (permalink)
5 (2)

Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Types

To anyone who deals with numbers in their software where 15 digits of accuracy (as opposed to 7) is important:

Just a heads-up – something I discovered late yesterday.

Given:
                Anything a programmer DOESN'T understand about the compiler he is using IS going to be a source of bugs.

I am about to arm you with something I didn't know (and is not clearly in my interpretation of Microchip compiler User's Guides) until late yesterday.

'float', 'double' and 'long double' are not strictly specified in the ANSI  C  specification.  What they REALLY are is left up to the implementer of the compiler.

In all the compilers I've been exposed to intended to make C programs run in on a computer with an 80x86 family of microprocessors have a pattern, largely because Intel came out with an 8087 math co-processor chip as an option on their IBM PC, and it was so popular that starting with the 80286 (I believe, or possibly the 80186 – I don't remember if the IBM PC AT still had the external 80187 chip option or not) they started building it INTO the chip.  This gave huge momentum to the IEEE 754 floating point standard which it implemented.

The IEEE 754 standard specified:

                "single precision" floating point had 32 bits with a 23-bit mantissa, 8-bit biased exponent, 1-bit sign and assigned meanings to those fields.

and

                "double precision" floating point had 64 bits with a 52-bit mantissa, … (etc.)

So, the long history of C compilers that I have worked with that generate output for the 80x86 chip family have adopted that same pattern with the naming of their type specifiers:

                'float'  and 'double' to mean SINGLE- and DOUBLE-precision IEEE 754 floating point respectively.

and later, 'long double' – also in NAME COORDINATION with the IEEE 754 standard with the "extra accurate" (80-bit) values that the 80x87 math co-processor chips dealt with internally to minimize rounding errors while computing results.  This became implemented in a number of the C compilers as optional numerical values that programmers could work with in their software.

-------------- Where I made an erroneous assumption -------------

                I didn't realize how much of this was actually weighted on the fact that the C compiler was generating output for an 80x86/87 family of chips.

Back to the ANSI  C  standard:

'float', 'double' and 'long double' are not strictly specified in the ANSI  C  specification.  What they REALLY are is left up to the implementer of the compiler.

And I'm going to give them the benefit of the doubt and say this is CORRECT and fits possible alternate hardware implementations and reasons for doing other things with them.

Enter Microchip C compiler family.

You already know:

                float                   32 bits    IEEE 754 single precision
                double               32 bits    IEEE 754 single precision (or 64 bits IEEE 754 double precision with  -fno-short-double)
                long double       64-bits    IEEE 754 double precision

(Note about Microchip documents:  specifically document DS51686B [MPLAB C Compiler for PIC32 MCUs User's Guide], page 8, section 1.5.4 – which is current as I write this on 26-Oct-2012 – lists a 'double' as 64 bits with no mention of the -fno-short-double switch) but my tests with this compiler show that the above is actually correct, and also shows that the document DS51686E [MPLAB XC32 C/C++ Compiler User's Guide], page 96, section 6.5 actually gets it right and lists 'double' as a 32-bit object.  In both cases, they do document the -fno-short-double switch correctly, but neither of them mentions it near the table that shows how many bits 'double' has.  Note:  all Microchip PIC32 compilers as well as the PIC24 compilers treat 'float', 'double' and 'long double' in exactly the same way – per the above, verified by my own tests.)

Here is what I missed:

The long norm (and thus became an assumption of mine) is that a constant numerical value, example 3.14159265358979 that the CONSTANT ITSELF is interpreted by the compiler as a DOUBLE PRECISION (64-bit value) and thus retains 15 significant digits.

WRONG!

What REALLY happens is this:

        WITH  -fno-short-double

                long double dd;
                dd = 3. 14159265358979;
                        // Here 'dd' contains all 15 significant digits.

        WITHOUT  -fno-short-double

                long double dd;
                dd = 3. 14159265358979;  
                        // Here 'dd' only contains an estimate close to 3. 141593!  8 significant digits lost!
                        // The constant itself is EVALUATED as a SINGLE-PRECISION floating point value FIRST before being assigned.

To illustrate what happened is more or less equivalent to this but without the 'f' variable:

                long double dd;
                float f;
                f = 3. 14159265358979;
                        // Yields 3. 141593 truncated as expected.

                dd = f;
                        // !!!  Ouch!  8 significant digits lost.

What to do to correct it:

                floating point CONSTANTS have an optional suffix 'L' which tells the compiler that you want the value to be interpreted as a DOUBLE-PRECISION value.

Thus:

        WITHOUT  -fno-short-double

                long double dd;
                dd = 3. 14159265358979L;  
                        // Here 'dd' retains all 15 digits as expected!
                        // Now the constant itself is EVALUATED as a DOUBLE-PRECISION floating point value FIRST before being assigned.

I thought you should be armed with this since you work with these compilers and it might be a mystery as to why you're not getting results you expected.

Note:  this is NOT WRONG – it was simply UNEXPECTED, and not covered clearly in my interpretation of any compiler User's Guides that I have seen.

I already sent this to Microchip documentation team directly for correction with explicit suggestions for correction in their compiler user's guides for C30, C32, XC16 and XC32 compilers.

Kind regards,
Victor Wheeler

#1

7 Replies Related Threads

    dan1138
    Super Member
    • Total Posts : 3106
    • Reward points : 0
    • Joined: 2007/02/21 23:04:16
    • Location: 0
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2012/10/26 14:52:39 (permalink)
    0
    You also need to be on the look out for bugs in the standard library code.
     
    This applies to XC16: http://www.microchip.com/forums/fb.ashx?m=676139
     
    Want to bet the PIC32 libs have no bugs?
    #2
    Abdul_Wahid
    Starting Member
    • Total Posts : 41
    • Reward points : 0
    • Joined: 2014/04/22 04:19:10
    • Location: 0
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/09/19 00:48:31 (permalink)
    +1 (1)
    If some one don't have time or don't want to read the long article written by vwheeler then summery of above is 
     
    long double = 3.14159265358979; // does not work due to a bug in XC Compilers.
    instead you should write as follows
    long double = 3.14159265358979L; // works very well
    #3
    mbrowning
    Just a Member
    • Total Posts : 1368
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/09/19 04:24:55 (permalink)
    +2 (2)
    Good summary! He did wander into an inaccurate history of Intel math co-processors. i486 was the first x86 with the co-proc built in, and a version was sold with it disabled (probably units where the co-proc failed test plus units where they didn't bother to test it). They sold a 487 to be used with the math disabled 486. The 487 was just another full featured 486, and it disabled the original 486 and took over all processing.

    Oh well - there's always next year
    #4
    simong123
    Lab Member No. 003
    • Total Posts : 1290
    • Reward points : 0
    • Joined: 2012/02/07 18:21:03
    • Location: Future Gadget Lab (UK Branch)
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/09/19 05:43:36 (permalink)
    +2 (2)
    Hmmm..the 8087 didn't actually conform to IEEE 754 (the 8087 came out 5 years before IEEE 754 was published)! The 80bit extended precision messed things up a bit wrt rounding intermediates unless you really needed to conform, which made it really slooooow as you had to round after each operation. And mbrowning is correct wrt the 486/486sx and 487!
     
    The Microchip compilers defaulting to 32bit doubles has long been a bone of contention, especially with the availability of floats if you need lower precision/more speed. I struggled for an age to figure out why an algorithem to calculate the position of the Moon worked fine on my PC (accurate to ~1 minute of arc), but positioned the Moon on the wrong side of the Earth on a PIC32.
     
    Moreover, wrt C32/XC32 -fno-short-double completely borked the standard libs from about C32 V2.01 (I think) until XC32 V1.43, which caused no end of problems. Thankfully this is now (as far as I have been able to acertain) fixed going forward, as long as you use Legacy Libc.
    #5
    CinziaG
    mind power
    • Total Posts : 3144
    • Reward points : 0
    • Joined: 2016/12/07 14:20:36
    • Location: Wien
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/09/19 05:45:48 (permalink)
    0
    Interesting these both!

    in 2018 you signed for your annihilation. in 2019 it will come ;) I promise
    #6
    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/10/05 04:25:57 (permalink)
    0
    Happened today:
    this piece of code, perfectly ok with C30 and XC16 (1.32) without optimizations

                                for(i=0; i<3; i++) {
                                    ValueF[i]+=gradient[i];
                                    CalcTriac(i,ValueF[i]);
                                     }

    won't work unless I unroll the loop. Basically ValueF is never added the correct "gradient" value, and shows crazy values (in both debugger and real life, i.e. leds brightness).
     
    CalcTriac is actually a macro that assignes the float to an 8bits-integer and then calls a function that updates the PWM - but, even if I considered it being the culprit... nothing changes even if I remove it. I had also tried
                                    ValueF=ValueF+gradient;
     
    Will try to log a ticket... I could say (but am not sure-sure) that in XC16 1.30 it was working...
     
     

    GENOVA :D :D ! GODO
    #7
    crosland
    Super Member
    • Total Posts : 1580
    • Reward points : 0
    • Joined: 2005/05/10 10:55:05
    • Location: Bucks, UK
    • Status: offline
    Re:Heads Up: Microchip Compiler Behavior with 'float', 'double' and 'long double' Data Ty 2017/10/05 04:50:10 (permalink)
    +1 (1)
    To illustrate what happened is more or less equivalent to this but without the 'f' variable:

                    long double dd;
                    float f;
                    f = 3. 14159265358979;
                            // Yields 3. 141593 truncated as expected.

                    dd = f;
                            // !!!  Ouch!  8 significant digits lost.

     
    No surprise there. Would you rather the extra 8 bits were assigned randomly, before assigning to dd?
    #8
    Jump to:
    © 2019 APG vNext Commercial Version 4.5