I'm not very experienced in the world of C programming, I just worked with arduino before, and I decided to get my feet more wet by doing a project on an ATtiny13 without using the Arduino IDE, only avr-gcc
via cli. Since Flash storage is at a premium, I am exploring various techniques to reduce code size. In my journey I came across multiplications and divisions, and I just became aware of the existance of libgcc
with its various arithmetic routines.
So I have this small test program:
unsigned char x = 200;
void main() {
x = x*2/3;
}
If I look at the map file that is generated, I see that the compiler is adding both __udivmodhi4
and __divmodhi4
to do this calculation, of which the signed division routine __divmodhi4
seems unnecessary. However, if use a short
instead of a char
, as such:
unsigned short x = 200;
void main() {
x = x*2/3;
}
This time I see only __udivmodhi4
being added in the map file, which is what I would expect. I also tried with uint8_t
from inttypes.h
and in that case it is adding back the signed division routine.
Could anyone please explain what is going on here? In all the above-mentioned cases, I would have expected the compiler to only add the unsigned division routine. Thanks!
Apparently in the C environment you are using,
int
is 16 bits. Then, whenx
is anunsigned char
, in the expressionx*2/3
:x
is promoted to anint
, as part of the usual arithmetic conversions applied for the operands ofx*2
.x*2
, both operands have typeint
, so no further promotions or conversions are needed, and the multiplication is performed withint
arithmetic./
areint
, so the division is performed withint
arithmetic.When
x
is anunsigned short
:unsigned short
is the same width asunsigned int
, sox
is not eligible for promotion toint
(promotion can only be to a type that can represent all values of the source type). It is promoted tounsigned int
(which is the same width asunsigned short
but is different for purposes of applying rules about types).x*2
,x
is anunsigned int
and2
is anint
, so the usual arithmetic conversions convert2
to anunsigned int
, and the multiplication is performed usingunsigned int
arithmetic.3
is converted to anunsigned int
, and the division is performed usingunsigned int
arithmetic.To keep the arithmetic unsigned when
x
is anunsigned char
, you can use any of the following expressions, among other options:(unsigned int) x * 2 / 3
.x * 2u / 3
.x * 2u / 3u
.