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.

Constant Expressions, ARM M3

Dear all,

I'm confused about the use of constant expressions. Or is it a compiler bug for the TI ARM v5.04?
Here is the sample code with the corresponding result in the line comment:

static float fTest;
static unsigned short uTest;
static unsigned long lTest;

fTest = 0.04; // 0.04 --> OK

uTest = fTest * 1000.0; // 40 --> OK

lTest = fTest * 1000.0;  // 39 --> WRONG
lTest = fTest * (float)1000.0; // 40 --> OK
lTest = (unsigned long)(fTest * 1000.0); // 39 --> WRONG
lTest = (unsigned long)(fTest * (float)1000.0); // 40 --> OK

------------------------------------------------------------------------------

Now my questions:

  1. Where can I find the specific use of constant expressions (e.g. 0x1234, 0L, 2LL, ...) in the compiler handbook?
  2. What is the reason for the wrong calculation?
    1. the floating point constant is interpreted as double, why is the cast to float necessary?
    2. why is there a different result with uTest and lTest?

Best regards,
Stefan

  • Those are not constant expressions.  A constant expression contains only constants, so the only constant expressions in your example are 0.04 and 1000.0

    However, that's not where the issue is.  To understand what you're seeing, you need to be aware of these facts:

    • The value 0.04 is not exactly representable in IEEE-754 binary floating-point format.  The value used in the compiler is approximately 0.039999999105930328369140625 (exactly 0x1.47ae14p-5, in C99 hex format).
    • 1000.0 is a double-precision float constant because you didn't use the 'f' suffix.  1000.0f is a single-precision float constant.
    • When you perform arithmetic on floating-point types, rounding occurs
    • When you perform arithmetic on mixed types, the compiler must promote them to a common type, usually the larger of the two.

    Now, what's going on is that you are multiplying the single-precision variable fTest by a double-precision value 1000.0.  This requires the compiler to promote the value in fTest to a double, which means adding a bunch of zeros on the end.  The compiler then rounds the double-precision result, which has a lot of zeros at the end, so it doesn't round up.  Then the value gets truncated to an integer type for assignment to an integer.

    When you instead multiply by a float-typed value, the compiler doesn't promote to double precision, and rounding just so happens to round up, giving you the value you desire.  However, you cannot rely on the value always being rounded up.  When dealing with floating-point values, you must always be aware that the values you are using may not be exactly representable, so you will sometimes get values that are not-quite-exactly the "algebraically correct" result.

  • Thanks for your reply. Up to now the compilers I used did not handle double precision folating types, so I wasn't aware of the difference using the suffix 'f' or not. I will have to take of that now!

    (You are right, it is not a constant expression I was listing in the example. It used to be one since the original code uses a macro for the fTest value.)

    For me it's still remarkable that the result between lTest und uTest is different since the same rounding should take place before it gets trunctuated to the integer type

  • There is a rounding step for the multiplication, but conversion to integer values does not round, it truncates.

  • I know that the cast will trunctuate, your answer didn't answer my question. I will precise my comment:

    "For me it's still remarkable that the result between lTest und uTest is different since the same rounding in the multiplication should take place before it gets trunctuated to the integer type"

  • That is odd, and I don't have a good answer for that.  I'll have to ask around.

  • I've submitted SDSCM00047077 to track this issue.