1

Disclaimer: I realize this is not the easiest or even best way to handle this and I am asking largely out of a sense of curiosity, not because I'm convinced this is the proper way. It's a fun puzzle I've been wrestling with for a day or so now and can't figure out (and can't seem to find a good answer online either!)

I'm working on a project with my Arduino (but this could apply to any hardware) which involves sensing temperature. I get the value, parse it, and have it stored in Celsius with a 1/100 scaling factor. e.g. the value 5.34C is stored in memory as the uint16_t 534. Thus printing this value is a matter of print (temp / 100 + "." + temp % 100). When I use it for logic in my program I need only compare it against another value with a 1/100 scaling factor and everything works great.

Let's pretend my Arduino or whatever system had no support for floating point numbers and I only had integers. How can I convert this number to Fahrenheit? I've tried several times to work it out on paper and it seems right but I always get an incorrect result when testing.

4

Multiply first, then divide; that will hide the integer round off error more:

in floating point (most accurate):

534.0 / 5.0 * 9.0 + 32.0 = 41.612

in integer arithmetic

534 / 5 * 9 + 3200 = 4154 (or 41.54 after scaling back)

because 534/5 = 106, in integer arithmetic, instead of 106.8; this round off error is then multiplied (magnified by multiplication), which accounts for the 41.54 vs 41.61 that we want.

How can I convert this number to Fahrenheit?

Multiplying first, then dividing:

534 * 9 / 5 + 3200 = 4161 (or 41.61 after scaling back)

alternatively, you can

  • scale up higher for the conversion, e.g. 1/1000 instead of 1/100

  • round up before the division, e.g. add 2 (or 3) before dividing by 5:

    (534+2) / 5 * 9 = 4163 (or 41.63 after scaling back)
    

Combining the "multiply first", and "the round up before divide" is reasonable:

(534 * 9 + 2) / 5 = 4161 (or 41.61 after scaling back)

And finally, combining all of the above (scaling to 1/1000, using multiply first, and round before divide):

(534 * 900 + 25) / 50 + 32000 = 41612 (or 41.612)

NOTE: this is not advocating fixed point arithmetic, per se — but rather just working a formula with the scaled number.  Truer to fixed point arithmetic would suggest 534 / 500 * 900 + 3200 = 4100.  (This would also benefit from multiply-first: 534 * 900 / 500 + 3200 = 4161.)

  • 2
    Once you go to fixed point, usually you want to use a larger number of bits for an intermediate result (after the multiplication) and check for overflow at some point when scaling back down. The integer byte size was not specified, but this is something we should always watch out for. – Frank Hileman Jun 19 '18 at 23:57
  • @FrankHileman, yes, good -- something to bear in mind! 32000, for example, is terribly close to the limit of a 16-bit signed integer; you'd want 32-bit arithmetic (and perhaps still check for/guard against overflow). – Erik Eidt Jun 19 '18 at 23:59
  • You can implement multi-word integer calculations (add/sub easy, mul/div harder) and even make the integer representations dynamic length :-) – ChuckCottrill Jun 20 '18 at 0:13
  • @ChuckCottrill That is an alternative to fixed point. – Frank Hileman Jun 20 '18 at 18:00
  • Thank you, my error was failing to also convert the +32 portion to +3200. Simple in retrospect but it goes to show that floats were invented for a reason. :) – brenzo Jun 20 '18 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.