I am trying to express a battery voltage as a percentage. My battery level is a (global) uint16 in mV. I have a 16-bit CPU. Here is my code:
static uint8 convertBattery(void){
uint16 const fullBattery = 3000; /* 3V = 3000mV */
uint8 charge;
charge = ((battery*100)/fullBattery);
return charge;
}
As you can see, I have factored in the integer division rounding down by multiplying the numerator by 100 first.
With a battery = 2756
my charge values calculates as 04
which is unexpected. I've spent ages on this rather trivial task and haven't made any progress. Could anyone suggest where the problem is?
Thanks.
Diagnosis
The value you expect is, presumably, 91.
The problem appears to be that your compiler is using 16-bit
int
values.You should identify the platform on which you're working and include information about unusual situations such as 16-bit
int
types. It is reasonable for us to assume 32-bit or 64-bit systems by default — they are by far the most common environments for questions on SO.You have
battery = 2756
. If that is multiplied by 100, it gives you 275600 in 32-bit precision, but in 16-bit precision, it gives 13546, which, when divided by 3000, yields 4, the observed answer.Here's code compiled with a 64-bit compiler that simulates the 16-bit calculation:
The output from it is:
To get a 16-bit calculation, I had to force the intermediate results into 16-bit variables. Yes, there are other more elaborate ways of writing the code, using
uint16_t
etc. And yes, the overflow in the assignment tor1
is strictly undefined behaviour, but my compiler (GCC 5.1.0 on Mac OS X 10.10.3) appears to be giving a sane interpretation to the undefined behaviour. This serves to demonstrate the point.Plausible fix
You should be able to work around it on your machine by using
long
:The
L
in100L
makes it along
value, which must be at least 32-bits in a conforming C compiler, so the multiplication must give along
value, and the divisor will be converted tolong
before the division, and the result will be along
(and the value will be 91), and you can assign that tocharge
safely.Alternatively, you could use one or more explicit
(long)
casts in the calculation if you think this is too subtle.