Applying C - Fixed Point Arithmetic |
Written by Harry Fairhead | |||||||
Monday, 11 May 2020 | |||||||
Page 3 of 3
Printing Fixed PointFinally, how do we print a fixed point value as a decimal? This is more difficult than you might imagine and is often implemented incorrectly. You can get the integer part of the result simply by shifting: temp>>8 and this can be printed as if it was just an integer, which is exactly what it is. You can also isolate the bits of the fractional part using a mask: temp&0xFF and at this point the temptation is to print this as if it was an int: printf("%d.%d \n",temp>>8,temp&0xFF); but it isn’t and this doesn’t work. The reason is that the bit pattern is weighted by inverse powers of 2 and the bit pattern doesn’t correspond to the usual decimal values. A simple example will illustrate the problem and give us the solution. Consider a fixed point value with the fractional part bit pattern: 10000000 which, if just treated as a decimal value, 128, gives a decimal fraction of: .128 which is obviously wrong. With the binary point inserted, the fractional part corresponds to: 0.10000000 and this is 0.5 in decimal, not 0.128. The fractional part of the fixed point represents a value in the range 0 to less than 1, but as an unsigned binary integer it represents a range from 0 to 2s. We can use this to obtain a decimal representation. For example, for the binary fraction: 0.10000000 the integer representation is: 10000000 which is 128, and this corresponds to the decimal fraction: 128/28 = 128/256 = 1/2 = 0.5 Of course, we can’t do the division to get 0.5 because we only have integer arithmetic, but if we multiply by 1000, a power of ten sufficient to give an integer, then we can do the entire calculation using integer arithmetic: 1000*128/28 = 1000*128/256 = 500 which when printed with a decimal point in front is: 0.500 You can see that the power of ten gives you the corresponding number of decimal places. Thus, if f is the fractional part converted to an integer, then 1000*f/2s is the decimal representation of the fraction. To print the temperature we use: printf("%d.%d \n",temp>>8,(temp&0xFF)*1000/(1<<8)); This works with obvious adjustments for any scale factor and any number of decimal places. It is important to note that the multiplication has to be performed before the division and this introduces the possibility of overflow. The rule is that the number of decimal places, d, has to satisfy: d*2s<232 or d<232-s for a 32-bit int. For example, if s is 16: d<232-16<216<65536 the largest value of d that avoids an overflow is 10,000. If s is 8 then d can be as large as 10,000,000. Omitted from this extract but in complete chapter:
Summary
Now available as a paperback or ebook from Amazon.Applying C For The IoT With Linux
Also see the companion book: Fundamental C <ASIN:1871962609> <ASIN:1871962617> Related ArticlesRemote C/C++ Development With NetBeans Getting Started With C/C++ On The Micro:bit To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.
Comments
or email your comment to: comments@i-programmer.info |
|||||||
Last Updated ( Saturday, 16 May 2020 ) |