Refresher on math with integers

I could use a refresher on integer math, specifically division and when or how you lose resolution/accuracy/digits. Anyone have a good link at hand?

For example, I would like to compute miles per hour as an integer result but the input to the calculation will be feet and clock time (hh:mm:ss). I'd like to stay with integer math to save space and time, but want to make sure to do the operations in the optimum order to not lose accuracy or incur error due to only using integers.

It's not that complicated, really. Just multiply first, then divide. Oh, and, watch out for overflows.

Your "clock time" precision is in seconds, so you are really just converting feet/second into MPH. Is that all you need?

If I'm not mistaken, you would multiply by 3600/5280.

unsigned long milesPerHour = 3600ul * feetPerSecond / 5280;

This will accomodate fps up to 2^32/3600 = 1193046 before overflow occurs.

In C, the largest datatype of the operands of leftmost, or "first" operation of an expression, dictates the type that will be used for subsequent calculations. That is why I didn't need to say 5280ul to force it to be an unsigned long. If milesPerHour is declared as an unsigned long, I can also:

unsigned long feetPerSecond = 100;
unsigned long milesPerHour =  feetPerSecond * 3600 / 5280;

because although something like 400*3600 is an (invalid, overflowing) int calculation because both operands are int types, feetPerSecond * 3600 is calculated using unsigned long arithmetic because the compiler sees that feetPerSecond is an unsigned long, so requires it. The result is also an unsigned long, so division by 5280 is also performed with unsigned long division. And so on and so forth up the Yangtze.

I would often do something like this:

const unsigned long SECONDS_PER_HOUR = 3600;
const unsigned long FEET_PER_MILE = 5280;
int feetPerSecond = 100;
unsigned long milesPerHour =  feetPerSecond * SECONDS_PER_HOUR / FEET_PER_MILE;

Notice how the 'feetPerSecond * SECONDS_PER_HOUR' forced unsigned long calculation and still looks clean and readable, and I was able to declare 'feetPerSecond' as an int.

Venturing a step further, I can actually declare the result as an int:

const unsigned long SECONDS_PER_HOUR = 3600;
const unsigned long FEET_PER_MILE = 5280;
int feetPerSecond = 100;
int milesPerHour =  int(feetPerSecond * SECONDS_PER_HOUR / FEET_PER_MILE);

because the result will always fit in an int, and all the intermediate calculations are in unsigned long arithmetic.

Avoid dividing by zero. Then the next most important rule is that n/m is zero, if n < m.

3600/5280 can be simplified down to reduce the chances of losing precision by overflow . ...

for example .. 15/22

Use unsigned int to get double the range .

Try some realistic numbers to see if multiplying first or dividing gives the best result in terms of accuracy and possible overflow rather than performing the calculation in one line ( a spreadsheet is handy here ) . Knowing how accurately you want the result ( car speedometers usually over read by about 5%) or how accurately can get the result from the input resolution is important too ( distance between pulses etc)

It is easy to dive into longs and think you are getting a better result than is possible ! ( just because the result says 45.68mph , doesn’t mean it is, it might be 47)

hammy:
3600/5280 can be simplified down to reduce the chances of losing precision by overflow . ...

for example .. 15/22

Yes, unfortunately it's not easy to get the GCD in compiler arithmetic so it could be automatic. But even an "eyeball" could show you that 360/528 would work. Anything like this, you need to inline document with a comment.