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.

Funny question about multiplication

Hello!

I found "weekend bug" , when I wrote couple of kilobytes long code. I writing in CSS4 and my uP is MSPf2242. Here is question:

char x  = 55, long z = undefined

If I multiplicate  z =  x * 1000000 ; The uP answer is z = 55000000

but   If I multiplicate  z =  x * 100000 ; The uP answer is z = 25712

Other part of code works fine, this is just result of algorithm.  Also result of multiplication x with 1, 10, 100, 1000 works fine.

 

So where is my god now?:)

  • That is what c compiler does to a 16-bit CPU.

    Is c your God? He is a jealous God, and his anger will burn against you, and he will destroy you from the face of the land.

  • So AntiGod mess up with my compiler?

    Hm...interesting.

  • There is nothing wrong with c. Nor anything is wrong with God. But if you worship c as your God, see what happens.

  • The question is not 'why does the second multiplication give a wrong result' but rather 'why does the compiler treat 1000000 as a long int constant while it treats 100000 as a short int constant.'.

    The size of the result variable is unimportant for the result. The C languag specification demands that all operands are expanded to the size of the largest operand before performing the operation. The result is also of the same size and THEN casted to the size of teh lvalue (the receiving variable).

    So 256*256 gives 0. becasue 256 is an int and therefore the result is an int (which overflows to 0), no matter whether the receiving variable is an int or a long int. It's stupid and inefficient but at least well defined.

    However, 100k should be considered a long constant as well as 1M and therefore the 55 should be expanded to long and then a 32x32 bit multiplication should take place (truncating the result to 32 bit). Which is stupid on an MSP since the hardware multiplier can to 8x16 multiplications with 32 bit result, or 8x32 bit multiplications with 64 bit result. But that's C.

    However, I wonder why 100k is obviously interpreted as short int constant and 1M is not. COnsistently, I would expect a warning for both and truncating the value to 16 bit, or treating both a slong constant.

    Anyway, to be sure, just add an 'L' after the constant value and the compiler will (or at least should) always treat it as 32 bit and therefore do a 32x32 multiplication.

    wait... that's strange... I just did a calculation and I get 27744 as the expected result of a truncated multiplication. So the problem must be more serious than just an operand/result truncation. I'd really like to see the assembly listing.

  • Jens-Michael Gross said:

    ... However, 100k should be considered a long constant ...

    He had a typo. He meant 10000 instead of 100000. Thus 55*10000 is 25712

  • old_cow_yellow said:
    He had a typo. He meant 10000 instead of 100000. Thus 55*10000 is 25712

    That explains why I got a different result :).
    And then there is no compiler oddity too. It's all correct C, strictly to the standard.

    char*int = int, even if then casted to a long int.

  • I have the same problem too, this is my code, I want to do 32 bits multiplication but the result is for 16 bits:
    unsigned long int x=43953, y=10, z;

    z=x*y;
    The result that I expect is z=439530 but I get is z=46314,
    any help will be appreciable

  • That is strange. Is 'long int" 32-bit wide? What assembly code did the compiler generate?

  • Mir Dali said:
    The result that I expect is z=439530 but I get is z=46314

    Since x and y are both long int, the result should be long and correct.
    But I dimly remember a different thread where it seemed that the debugger has a problem showing the content of long int variables under some circumstances. It only presented the lower word content.

    So maybe the result is correct but the debugger just fails to show it. It would be interesting to know where in memory 'z' lies. Maybe on a ram page border and the dat afetch algorithm of the debugger fails then?

    If you use PRINTF to print the result, then be sure to add the long modifier to the format string (%lu instead of %u), else printf will only print a short int and ignore the additional 2 bytes you passed as parameter.

     

  • The long is in 4 bytes, I try used printf with lu and it gives me z=4294751466
    This is the assembly code:
        0x4056    <main>:        mov    #14580,    r1    ;#0x38f4
    -    0x405a    <main+4>:        mov    r1,    r4   
    -    0x405c    <main+6>:        mov    #-21583,8(r4)    ;#0xabb1, 0x0008(r4)
    -    0x4062    <main+12>:        mov    #0,    10(r4)    ;r3 As==00, 0x000a(r4)
    -    0x4066    <main+16>:        mov    #10,    4(r4)    ;#0x000a, 0x0004(r4)
    -    0x406c    <main+22>:        mov    #0,    6(r4)    ;r3 As==00, 0x0006(r4)
    -    0x4070    <main+26>:        call    #0x417e   
    -    0x4074    <main+30>:        mov    8(r4),    r10    ;0x0008(r4)
    -    0x4078    <main+34>:        mov    10(r4),    r11    ;0x000a(r4)
    -    0x407c    <main+38>:        mov    4(r4),    r12    ;0x0004(r4)
    -    0x4080    <main+42>:        mov    6(r4),    r13    ;0x0006(r4)
    -    0x4084    <main+46>:        push    r2       
    -    0x4086    <main+48>:        dint           
    -    0x4088    <main+50>:        call    #0x711c   
    -    0x408c    <main+54>:        pop    r2       
    -    0x408e    <main+56>:        mov    r14,    0(r4)    ;0x0000(r4)
    -    0x4092    <main+60>:        mov    r15,    2(r4)    ;0x0002(r4)
    -    0x4096    <main+64>:        push    2(r4)        ;0x0002(r4)
    -    0x409a    <main+68>:        push    @r4       
    -    0x409c    <main+70>:        push    #16458        ;#0x404a
    -    0x40a0    <main+74>:        call    #0x6934   
    -    0x40a4    <main+78>:        add    #6,    r1    ;#0x0006
    -    0x40a8    <main+82>:        push    #2        ;r3 As==10
    -    0x40aa    <main+84>:        push    #16465        ;#0x4051
    -    0x40ae    <main+88>:        call    #0x6934   
    -    0x40b2    <main+92>:        add    #4,    r1    ;r2 As==10
    -    0x40b4    <main+94>:        push    #4        ;#0x0004
    -    0x40b8    <main+98>:        push    #16465        ;#0x4051
    -    0x40bc    <main+102>:        call    #0x6934   
    -    0x40c0    <main+106>:        add    #4,    r1    ;r2 As==10
    -    0x40c2    <main+108>:        push    #2        ;r3 As==10
    -    0x40c4    <main+110>:        push    #16465        ;#0x4051
    -    0x40c8    <main+114>:        call    #0x6934   
    -    0x40cc    <main+118>:        add    #4,    r1    ;r2 As==10
    -    0x40ce    <main+120>:        push    #4        ;#0x0004
    -    0x40d2    <main+124>:        push    #16465        ;#0x4051
    -    0x40d6    <main+128>:        call    #0x6934   
    -    0x40da    <main+132>:        add    #4,    r1    ;r2 As==10
    -    0x40dc    <main+134>:        add    #12,    r1    ;#0x000c
    -    0x40e0    <main+138>:        br    #0x71b8

  • Hmmm,

       0x4056    <main>:        mov    #14580,    r1    ;#0x38f4
    stack init
    -    0x405a    <main+4>:        mov    r1,    r4   
    copy stack frame pointer (superfluous for main(), but well...)
    -    0x405c    <main+6>:        mov    #-21583,8(r4)    ;#0xabb1, 0x0008(r4)
    -    0x4062    <main+12>:        mov    #0,    10(r4)    ;r3 As==00, 0x000a(r4)
    load 43953 to long int y
    -    0x4066    <main+16>:        mov    #10,    4(r4)    ;#0x000a, 0x0004(r4)
    -    0x406c    <main+22>:        mov    #0,    6(r4)    ;r3 As==00, 0x0006(r4)
    load 10 to x
    -    0x4070    <main+26>:        call    #0x417e   
    no idea what this is.
    -    0x4074    <main+30>:        mov    8(r4),    r10    ;0x0008(r4)
    -    0x4078    <main+34>:        mov    10(r4),    r11    ;0x000a(r4)
    -    0x407c    <main+38>:        mov    4(r4),    r12    ;0x0004(r4)
    -    0x4080    <main+42>:        mov    6(r4),    r13    ;0x0006(r4)
    move the operands to teh multiplication source registers
    -    0x4084    <main+46>:        push    r2       
    -    0x4086    <main+48>:        dint           
    block itnerrupts
    -    0x4088    <main+50>:        call    #0x711c   
    do the multiplication
    -    0x408c    <main+54>:        pop    r2       
    restore previous interrupt state
    -    0x408e    <main+56>:        mov    r14,    0(r4)    ;0x0000(r4)
    -    0x4092    <main+60>:        mov    r15,    2(r4)    ;0x0002(r4)
    write the result to z
    -    0x4096    <main+64>:        push    2(r4)        ;0x0002(r4)
    -    0x409a    <main+68>:        push    @r4       
    put z on teh stack
    -    0x409c    <main+70>:        push    #16458        ;#0x404a
    put the address of the format string to stack
    -    0x40a0    <main+74>:        call    #0x6934   
    do the printf.
    (the rest is, well, don't know, as I don't know the source code)


    Everythinglooks okay so far. Unless you have made a mistake with the format string (shouldn't).

    So what's going wrong, I don't know. Did you include the correct MSP430x header? Is the project set to the correct MSP type?

    Do you run this code in simulator or emulator (if using IAR)? In simulator, there might be a bug with the simulated hardware multiplier ( I don't really believe it, but...)

  • I'm working with wsim which is a simulator that compile and run on unix like plateform. It includes Ti MSP430 model:f1611. My gcc version is 4.4.3
    This is my source code:
    #include <stdio.h>
    #include "uart1.h"
    #include <stdlib.h>
    #include <stdint.h>
    #include <string.h>
    #include <math.h>
    int putchar(int c)
       {
       return uart1_putchar(c);
       }

    int main(void)
       {
     
    unsigned long int  x=43953, sig=10, z;
     
    uart1_init();
    z=x*sig;
    printf("x1=%u\n", z);
    printf("x1=%lu\n", z);
    printf("%d \n", sizeof(unsigned short int));
    printf("%d \n", sizeof(long int));
    printf("%d \n", sizeof(int));
    printf("%d \n", sizeof(unsigned long int));

         }

  • Interesting stuff.  Can you tell gcc not to use the hardware multiplier in the '1611 just as a quick test?  Maybe wsim doesn't simulate the hardware multiplier quite right.  I wouldn't be too surprised by that.

    Jeff

  • Mir Dali said:

    ...
    This is the assembly code:
        0x4056    <main>:        mov    #14580,    r1    ;#0x38f4
    -    0x405a    <main+4>:        mov    r1,    r4   
    -    0x405c    <main+6>:        mov    #-21583,8(r4)    ;#0xabb1, 0x0008(r4)
    -    0x4062    <main+12>:        mov    #0,    10(r4)    ;r3 As==00, 0x000a(r4)
    -    0x4066    <main+16>:        mov    #10,    4(r4)    ;#0x000a, 0x0004(r4)
    -    0x406c    <main+22>:        mov    #0,    6(r4)    ;r3 As==00, 0x0006(r4)
    -    0x4070    <main+26>:        call    #0x417e   
    ...

    I am really amazed. It looks like the code generated by the compiler is trying to pass two parameters to a subroutine. The parameters are 0x0000ABB1 (43953) and 0x0000000A (10). The subroutine is located at 0x417e.

    Could that be right? Isn't this rather hazardous? Was the interrupt disable?

    Note: This has noting to do with the unexpected display value of z.

  • old_cow_yellow said:
    I am really amazed. It looks like the code generated by the compiler is trying to pass two parameters to a subroutine.

    That's normal. The 1611 has a 16x16 hardware multiplier. So 16x16 multiplications are usually handled inline, but for everything above (and for no HW multiplier), the linker links low-level multiplicaiton functions, which get two parameters and return one. inlining 32 bit multiplicaitons wouldn't be much faster but much bigger.
    However, on MSPGCC, these functions do not exactly follow the specificaitons for standard function calls (especially with respect to clobbered registers), so the compiler can better optimize around the function call.
    I don't know for other GCC variants.

    p.s.: MSPGCC has a compiler option to not use the hardware multiplier at all (then the functions that are linked to software multiplication) or to not use it inside ISRs (so no interrupt blocking is required or generated around inlined multiplications)

    However, the mspgcc (at least the verisons based on GCC 3) did not support the 32 bit hardware multiplier, so teh embedded multiply functions used the ineffective 16 bit fallback.

    However, I wrote some inline functions which allow a 16x16 multiplicaiton with 32 bit result without need to cast the operands to 32 bit and do a 32x32 multiplication It's way faster, but not possible with an '*' operator based on C(++) standards.

  • Can you please give me the link for the inline functions
    thanks

  • Mir Dali,

    I downloaded source code for the wsim MSP430 simulator.

    The simulation of the hardware multiplier does have some bugs in it.  They were not careful enough with the type casting between 16- and 32-bit signed and unsigned values.

    These bugs are causing your multiplication anomalies.  The real MSP430 hardware will not have these problems.

    Jeff

  • If you cannot trust your tools... :(

  • Just a quick update to say that the bug is now fixed, svn revision 632, and it is already available for download.

    Jeff

**Attention** This is a public forum