## 16/32-bit math and casting

11 posts / 0 new
Author
Message

Is there a tutorial on integer math, when to, when not to cast?
I'm using the Tiny44 with AVR Studio 4.16, and the latest Win-AVR.

I want to square a 16-bit variable. It's my 10-bit ADC value and place the result into a 32-bit variable. Thus having a maximum of 20 significant bits in a 32-bit variable.

```uint16_t adcvalue

```

do I need to force 32-bit math?

```uint16_t adcvalue

```

When do I need to cast a lower bit variable to a higher bit variable during math operations.
A tutorial somewhere would be great. I searched the turorials section, but came up empty.
Thanks.

Jim M., Rank amateur AVR guy.

The basic rules for this case are quite simple.

A = B * C

1)
The math is done independent of the type of the variable you store the result in.
The type of A does not influence the way B*C is calculated. Only the result is converted to the type of A.

2)
The type of the "biggest" operand is the type the calculation is done in.
E.g. if B is 16-bit and C 8-bit, then C is promoted to 16-bit and the calculation is done in 16-bit.

So in your case you need to cast at least one operand to 32-bit, otherwise the calculation is done in 16-bit with the risk of overflows.

Stefan Ernst

Last Edited: Fri. Jul 10, 2009 - 01:52 AM

with one addition to Stefan's comment... all calculations if less than 16bit operands are used, will be promoted to 16bit for the calculation. Unless forced otherwise by casting.

There are some other subtleties, but that should be good enough to get you started.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

glitch wrote:
There are some other subtleties, but that should be good enough to get you started.
Yes, that's why I called it "basic rules". ;-)

Stefan Ernst

That makes sense.

So casting just one of the factors in the right hand side of the equation as 32-bit

```adcsquared = (uint32_t)adcvalue * adcvalue;
```

The calculation will be done in 32-bit. Not a possibly overflowed 16-bit result.
Just what I was looking for.
Thanks.

Jim M., Rank amateur AVR guy.

sometimes life is easier as you expect......

regards

glitch wrote:
with one addition to Stefan's comment... all calculations if less than 16bit operands are used, will be promoted to 16bit for the calculation. Unless forced otherwise by casting.
In principle, casting does not defeat the promotion.
Casting is simply a unary operation with a well-defined result type.
Quote:
There are some other subtleties, but that should be good enough to get you started.
The main one is that in case of a tie, unsigned is favored.

The promotion to 16-bit occurs because
that is the size of an int in avr-gcc.
C does its arithmetic in sizes of int and larger.

Iluvatar is the better part of Valar.

skeeve wrote:
The promotion to 16-bit occurs because that is the size of an int in avr-gcc. C does its arithmetic in sizes of int and larger.
...unless told (through casting) to do it in 8-bit.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

stu_san wrote:
skeeve wrote:
The promotion to 16-bit occurs because that is the size of an int in avr-gcc. C does its arithmetic in sizes of int and larger.
...unless told (through casting) to do it in 8-bit.
No. In principle, the computation is never done in 8-bit.
The result, 16 bits or larger, may be cast to a smaller type.
Admittedly, there aren't many cases where the
distinction would matter, even in principle.
Here is one:
```signed char fred=60;
unsigned char greg=(unsigned char)(fred*fred);```

The value in greg would be implementation
defined without int promotion.
The result of signed overflow is implementation defined.

Iluvatar is the better part of Valar.

Quote:
No. In principle, the computation is never done in 8-bit

That depends on what you mean by "done in 8-bit". I put this code into avr-gcc (with both value1 and value2 defined as uint8_t and res defined as uint16_t):

`res = value1 * value2;`

and I got this result:

```MUL       R24,R18        Multiply unsigned
MOVW      R24,R0         Copy register pair
CLR       R1             Clear Register
STD       Y+2,R25        Store indirect with displacement
STD       Y+1,R24        Store indirect with displacement```

Obviously the compiler took into account that value1 and value2 were 8 bit. It didn't promote them to 16 bit first, then do a 16 bit by 16 bit multiply.

But even this code:

`PORTB = value1 * value2;`

done on a tiny13 that has no MUL opcode resulted in this:

```CLR       R0             Clear Register
SBRC      R24,0          Skip if bit in register cleared
LSL       R22            Logical Shift Left
BREQ      PC+0x03        Branch if equal
LSR       R24            Logical shift right
BRNE      PC-0x05        Branch if not equal
MOV       R24,R0         Copy register```

Obviously an 8 bit by 8 bit multiply routine.

Regards,
Steve A.

The Board helps those that help themselves.