This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/MSP432P4111: Double floating point inequality expression evaluates incorrectly

Part Number: MSP432P4111

Tool/software: TI C/C++ Compiler

I found what appears to be a Compiler bug. When evaluating a simple inequality expression (x < y) using double float numbers, the evaluation is sometimes wrong (e.g. evaluates False when it should be TRUE).  The fault case I found involved comparing a constant with a variable (that was set to MIN_DOUBLE).  Other cases fail or succeed depending on values used.  The issue is only seen when using the ccs TI Compiler, no fault seen using the gcc GNU Compiler.  I need help understanding and solving this problem.  I included a very simple code example below loaded onto an Evaluation Board (MSP-EXP432P4111) that illustrates the problem.

Components:

         Code Composer Studio:  CCS ver 7.2.0.00013

         Compiler:  TI v18.1.4.LTS

         Compiler:  GNU v 6.3.1 (Linaro)

/*******************************************************************************
*
*
*******************************************************************************/
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include <stdbool.h>
#include <stdio.h>

#define     MIN_DOUBLE        (double)4.94065645841247E-324

double    x;
bool    bool1, bool2, bool3, bool4;

int main(void)
{
    MAP_WDT_A_holdTimer();

    x = MIN_DOUBLE;
    bool1 = (0.0 < x);                                    // should evaluate to TRUE, but incorrectly evaluates to FALSE (when using TI Compiler)
    bool2 = (0.0 < MIN_DOUBLE);            // should evaluate to TRUE, correctly evaluate to TRUE
    bool3 = (0.0 > x);                                    // should evaluate to FALSE, correctly evaluate to FALSE
    bool4 = (0.0 > MIN_DOUBLE);            // should evaluate to FALSE, correctly evaluate to FALSE

    printf("bool1 = %d\r", bool1);
    printf("bool2 = %d\r", bool2);
    printf("bool3 = %d\r", bool3);
    printf("bool4 = %d\r", bool4);
    fflush (stdout);
    while(1);
}

Output from CCS Console

bool1 = 0     // this value is wrong

bool2 = 1    

bool3 = 0

bool4 = 0

  • AFAICT, DBL_MIN (from float.h) is 2.225+E-308 which is larger than your MIN_DOUBLE above. The compiler will probably just set it to 0.0.
  • Keith Barkley said:
    AFAICT, DBL_MIN (from float.h) is 2.225+E-308 which is larger than your MIN_DOUBLE above.

    According to Double-precision examples 2.225+E-308 is "Min. normal positive double". Whereas MIN_DOUBLE (double)4.94065645841247E-324 is "Min. subnormal positive double".

    I ran the example program on a MSP432P401R built with TI compiler v18.1.4.LTS.

    The following expression, comparing the constant zero and double x:

        bool1 = (0.0 < x);                                    // should evaluate to TRUE, but incorrectly evaluates to FALSE (when using TI Compiler)

    Was converted into a call to the run time library __aeabi_cdcmple assembly function.

    The first argument was 0 00000000000 00000000000000000000000000000000000000000000000000002 = positive zero.

    The second argument was 0 00000000000 0000000000000000000000000000000000000000000000000001 ≈ 4.9406564584124654 × 10−324 (Min. subnormal positive double)

    I.e. the compiler is correctly storing MIN_DOUBLE in a double variable, but the run time library __aeabi_cdcmple library function seems to be incorrectly treating the Min subnormal positive double as zero.

    In the following expression, since both arguments to the comparison are constant the compiler calculates the correct comparison result at compile time, rather than comparing the double values at run-time:

        bool2 = (0.0 < MIN_DOUBLE);            // should evaluate to TRUE, correctly evaluate to TRUE

  • link:

    Type Size Representation Minimum Maximum

    signed char 8 bits ASCII -128 127

    char (1) 8 bits ASCII 0 (1) 255 (1)

    unsigned char 8 bits ASCII 0 255

    bool, _Bool 8 bits ASCII 0 (false) 1(true)

    short, signed short 16 bits Binary -32 768 32 767

    unsigned short, wchar_t (2) 16 bits Binary 0 65 535

    int, signed int 32 bits Binary -2 147 483 648 2 147 483 647

    unsigned int 32 bits Binary 0 4 294 967 295

    long, signed long 32 bits Binary -2 147 483 648 2 147 483 647

    unsigned long 32 bits Binary 0 4 294 967 295

    long long, signed long long 64 bits(3) Binary -9 223 372 036 854 775 808 9 223 372 036 854 775 807

    unsigned long long 64 bits(3) Binary 0 18 446 744 073 709 551 615

    enum (TI_ARM9_ABI and TIABI only) (4) 32 bits Binary -2 147 483 648 2 147 483 647

    float 32 bits IEEE 32-bit 1.175 494e-38(5) 3.40 282 346e+38

    double 64 bits(3) IEEE 64-bit 2.22 507 385e-308(5) 1.79 769 313e+308

    long double 64 bits(3) IEEE 64-bit 2.22 507 385e-308(5) 1.79 769 313e+308

    pointers, references, pointer to data members 32 bits Binary 0 0xFFFFFFFF

    (1) "Plain" char has the same representation as either signed char or unsigned char. The --plain_char option specifies whether "plain" char is signed or unsigned. The default is unsigned.

    (2) This is the default type for wchar_t. You can use the --wchar_t option to change the wchar_t type to a 32-bit unsigned int type.

    (3) 64-bit data is aligned on a 64-bit boundary.

    (4) For details about the size of an enum type, see Section 5.5.1. Also see Table 5-2 for sizes.

    (5) Figures are minimum precision.

    Negative values for signed types are represented using two's complement.

    What is allowed for subnormalized values? I do not know. 

  • I've seen this double equality problem in my time with the MSP432P4111, also.

    Chester Gillon said:

    The first argument was 0 00000000000 00000000000000000000000000000000000000000000000000002 = positive zero.

    The second argument was 0 00000000000 0000000000000000000000000000000000000000000000000001 ≈ 4.9406564584124654 × 10−324 (Min. subnormal positive double)

    I.e. the compiler is correctly storing MIN_DOUBLE in a double variable, but the run time library __aeabi_cdcmple library function seems to be incorrectly treating the Min subnormal positive double as zero.

    I just got around it by redefining the E-324 constant as E-308, as I didn't think it mattered that my lowest positive value was E-308 vs. E-324. My calculations don't need to be that precise.

    Do you think there needs to be a bug report filed for this phenomenon? Sounds like this needs to be addressed by TI. My code I was working on matched the result I was trying to get from another programming language only when I tried it on a GCC compiled example from TIREX. I tried it on a CCS compiled example form TIREX and it also failed to match because of this double equality error. Not a big problem for me, but it sounds like it wasn't just me finding this problem.

    Thanks for the post,

  • Because it does not have a suffix, the constant has type "double."  The fact that you cast it to double has no effect.

    For MSP430 EABI, double is 64-bit IEEE double precision

    For 64-bit IEEE, the constant "4.94065645841247E-324" is a denormal/sub-normal number.  This means it has an different representation "normal" floating-point numbers because it is too small to represent with the normal form.

    The TI tools specifically note that denormal numbers are not supported. This means that even if you get the compiler to accept a denormal number, there is no guarantee that you'll get the same value as a platform that does support denormal numbers.

    In most cases, denormal numbers are "flushed" to zero; that is, denormal inputs are converted to zero before each computation.  "Flush-to-zero" is an accepted mode in the IEEE standard, and it is what TI hardware that has floating-point support does.

    It is probably a bug that some parts of the TI toolset don't handle denormal constants in exactly the same way; that is, it is probably a bug that (0.0 < x) evaluates to TRUE.  We can enter a bug that the TI toolset has failed to flush this constant to zero before the comparison, but I can tell you it's not going to be fixed anytime soon.

  • Archaeologist,
    many thanks!