This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Arithmetic using numeric constants

Hello,

We came across a mystery/bug yesterday. This was tested with C51 v9.53 (Simplicity Studio) and 9.56 (uVision).

unsigned char data c;
unsigned int data d;

// The values 0x7B and 0xFF7B are wrong
c = (200 - 50) * (255 - 0) / (255 - 50);
0000738d:   MOV     65H, #7BH

d = (200 - 50) * (255 - 0) / (255 - 50);
00007390:   MOV     66H, #0FFH
00007393:   MOV     67H, #7BH


//These are correct
c = (200 - 50) * (255u - 0) / (255 - 50);
0000738d:   MOV     65H, #0BAH

d = (200 - 50) * (255u - 0) / (255 - 50);
00007390:   MOV     66H, #00H
00007393:   MOV     67H, #0BAH

The uVision docs say that numeric constants default to 16 bits. Is this not true? Or is this an issue with the "Constant Folding" optimizing step?

Any insights appreciated,
Darren

Parents
  • unsigned char data c;
    unsigned int data d;

    // The values 0x7B and 0xFF7B are wrong

    Not exactly.

    c = (200 - 50) * (255 - 0) / (255 - 50);

    All the constants are "upgraded" to 16 bit values, but there is nothing indicating to upgrade past integers (signed) to unsigned integers. Some of these "positive" numbers become "negative" numbers during the calculation, thus giving what appears to be invalid values. In the version you say is correct, you instructed the compiler to upgrade to unsigned 16 bit integers and got what you were expecting (but not what you had originally asked for)

    (200 - 50) is 150
    (255 - 0) is 255
    150 * 255 is 38,250. This is a "negative" number. (it is 0x956A or -27286)

    For division on this processor, If you want to divide with/by a negative number, we take the 2's compliment of the number(s) and make any signed adjustments when we are done.

    0x956A is converted to 0x6A95 or +27286

    26286 / 205 is 133.1024.

    Since we divided a negative number by a positive number, we know we need to take the 2's complement of the result to get the "correct" answer.

    133.1024 is rounded to 133 and becomes -133
    -133 as a 16-bit integer is 0xFF7B

Reply
  • unsigned char data c;
    unsigned int data d;

    // The values 0x7B and 0xFF7B are wrong

    Not exactly.

    c = (200 - 50) * (255 - 0) / (255 - 50);

    All the constants are "upgraded" to 16 bit values, but there is nothing indicating to upgrade past integers (signed) to unsigned integers. Some of these "positive" numbers become "negative" numbers during the calculation, thus giving what appears to be invalid values. In the version you say is correct, you instructed the compiler to upgrade to unsigned 16 bit integers and got what you were expecting (but not what you had originally asked for)

    (200 - 50) is 150
    (255 - 0) is 255
    150 * 255 is 38,250. This is a "negative" number. (it is 0x956A or -27286)

    For division on this processor, If you want to divide with/by a negative number, we take the 2's compliment of the number(s) and make any signed adjustments when we are done.

    0x956A is converted to 0x6A95 or +27286

    26286 / 205 is 133.1024.

    Since we divided a negative number by a positive number, we know we need to take the 2's complement of the result to get the "correct" answer.

    133.1024 is rounded to 133 and becomes -133
    -133 as a 16-bit integer is 0xFF7B

Children