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

Arm7 long multiply in C

Hallo to everyone.

My problem is to have the upper 4 Byte result of a multiply.
For example:

unsigned int a=1000000000;//only example value
unsigned int b=10000000000;
unsigned int c;



the result of a*b = 0x8AC7230489E80000

How to get the upper 4 Bytes:
0x8AC72304
How to get the lower 4 Bytes:
0x89E80000

Can anybody help?
I have to programm a super-fast-interrupt-routine for an microcontroller, there is not enough time for floating point.

  • You'd want one of the long long integer types then.

  • Hello,

    long long may be a way, but i only need a 32-bit result, the upper 4 byte or the lower 4 byte.
    Maybee someone has a programm-example :-)

    Regards

  • So extract the MSLong or LSLong of the 64-bit result. Shifting and masking, or unions work on long longs too.

  • union U64_ {
        unsigned long long  ull;
        unsigned long       ul[2];
    };
    
    unsigned long multiplier;
    unsigned long multiplicand;
    union U64_    product;
    unsigned long product_msl;
    unsigned long product_lsl;
    
    product.ull = (unsigned long long)multplicand * multiplier;
    product_msl = product.ul[1];    /* Assuming little-endian */
    product_lsl = product.ul[0];    /* Assuming little-endian */
    

  • Quickly typed, not tested :-(

    s/multplicand/multiplicand/

  • Hi, i tried several version of something like

    unsigned int a=3162277661;
    unsigned int b=3162277660;
    unsigned int c;
    c=((a*b)>>32)&0xFFFFFFFF;
    


    but i get wrong result or

    ARM COMPILER V2.40c, ADIDEMO\LONG_LONG_7.C: '>>': out of range shift factor has been truncated
    *** WARNING C200 IN LINE 16 OF ADIDEMO\LONG_LONG_7.C: '>>': out of range shift factor has been truncated

  • Regarding the shift operator:

    "If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined."

  • but i get wrong result or

    No, the result is not wrong, what you're doing is wrong. You're expecting the compiler to automatically expand the result to 64 bits, which it won't do.

    Either you do it in assembly (ARM has very nice multiply instructions that can calculate the upper part, the lower part, or all 64 bits of a 32 bit * 32 bit multiplication), or you tell the compiler that it needs to use a 64 bit integer datatype. I wouldn't expect it to come up with optimal (super-fast) code however.

  • Define them as long long int (unsigned int if you desire)

    long long int a;
    long long int b;
    long long int c;

    int lower32;
    int upper32;

    c = a * b;

    lower32 = *((u32 *) &c);
    upper32 = *(((u32 *) &c) + 1); // little endian assumed

    Don't worry the the last assignments look ugly. The compiler will easily optimize this to a direct 32-bit fetch of the value.

  • I hope you have a program-example in assembler or C
    for me, how to get this upper 4byte of the multiplikation (int * int).

    Of cause i know
    The Compiler is allways right,
    i'am allways doing wrong...

    but how to change to the other side ;-) ;-) ;-)

    :-)

  • "I hope you have a program-example in assembler or C"

    Any particular reason you don't like the examples provide thusfar?


  • Any particular reason you don't like the examples provide thusfar?

    as i havent used unions before, i'am afraid of getting large and slow code.

    I will test it tomorrow, i promise :-)

  • I hope you have a program-example in assembler or C

    Actually, I don't.

    I have a copy of the ARM instruction set here, which describes the MUL (32x32 bit multiplication, least significant 32 bits as result) and SMMUL (32x32 bit multiplication, most significant 32 bits as result) assembly instructions.

    If you don't want to get your hands dirty doing assembly, there's enough examples here already that show how to do it in C using long long ints. Of course, this might not be super-fast, depending on the compiler. Maybe the compiler is smart enough to recognize something along the lines of

    unsigned long long r;
    unsigned long a, b;
    ...
    r = (unsigned long long) a * b;
    

    as a single 32x32 multiplication, maybe it is not. Take your chances.

    as i havent used unions before,

    Maybe you need to learn to walk before trying to run a marathon ?

    i'am afraid of getting large and slow code.

    Since you're not familiar with the internal architecture of ARM, how do you know that something will generate large and slow code ?

    Of course, you can also try to do everything with 32 bit integers, using some elementary school-level maths:

    (off the top of my head)

    unsigned int a, b, ah, al, bh, bl, r1, r2, r3, r4, msw, lsw;
    ...
    ah = a >> 16;
    al = a & 0x0000FFFF;
    bh = b >> 16;
    bl = b & 0x0000FFFF;
    
    r1 = ah * bh;
    r2 = ah * bl;
    r3 = al * bh;
    r4 = bl * bl;
    
    msb = r1 + r2 >> 16 + r3 >> 16;
    r2 = r2 << 16;
    r3 = r3 << 16;
    lsb = r2 + r3;
    if((lsb < r2))
    {  msb = msb + 1;  }
    lsb += r4;
    if((lsb < r4))
    {  msb = msb + 1;  }
    

    Do you think this kludge is going to be faster (on an ARM) than using long long ints ?

  • r4 = bl * bl;
    

    should be

    r4 = al * bl;
    

    and all occurrences of msb/lsb should be msw/lsw.

  • Hello,
    this is perfect but very slow code...

    long long int a;
    long long int b;
    long long int c;
    
    int lower32;
    int upper32;
    
    c = a * b;
    
    lower32 = *((u32 *) &c);
    upper32 = *(((u32 *) &c) + 1); // little endian assumed
    

    i had to replace u32 by unsigned int :-)

    Another side effect:
    Writing

    a=3162277660;// = 0XBC7C871C
    


    gives the result a=0XFFFFFFFFBC7C871C

    a=0xBC7C871C;// = 3162277660
    


    gives the result a=0X00000000BC7C871C