CCS News

Tech Note: Optimize Code using Fixed-Point instead of Floating-Point with the CCS C Compiler

Thursday 15 June, 2017

Most embedded microprocessors and microcontrollers, including the Microchip PIC® MCU families, do not contain a hardware implemented floating-point calculator. This means that all float calculations must be implemented in software using integer arithmetic, which is very resource intensive. In turn, performance heavy applications and resource limited platforms may be unable to utilize floating point numbers in their implementation. The solution for this is to instead use fixed-point arithmetic, which is simplified by the CCS C Compiler's fixed type feature.

Fixed-point arithmetic is an implementation that uses a scaling factor to represent decimal numbers in integer form. The CCS C Compiler implements this using 16 or 32 bit integers and a scaling factor of 10-n. A fixed type can be declared as follows:

int16 _fixed(n) foo; where 0 < n < 6
int32 _fixed(n) foo; where 0 < n < 11

The value n determines the number of decimal places of accuracy the variable will contain, as well as the maximum representable value. Since n is part of the type and determines what instructions are generated, it must be given as a constant at compile time.

For int16 _fixed(2) : Max = 65,535 * 10-2 = 655.35
For int32 _fixed(5) : Max = 4,294,967,295 10-5 = 42,949.67295

The binary/hexadecimal representation for any number can then be determined by multiplying by the inverse of the scaling factor and converting.

237.16 * 10-2 = 23716 = 0x5CA4

The fixed type is compatible with another fixed type of the same n for the 4 basic arithmetic operations. They are not compatible with fixed types with a different n.

int16 _fixed(1) f1 = 5.5;
int16 _fixed(1) f2 = 2.5;
f1 + f2; //evaluates to 8.0
f1 - f2; //evaluates to 3.0
f1 * f2; //evaluates to 13.7
f1 / f2; //evaluates to 2.2

It can also perform arithmetics with literals and cast integers.

int16 _fixed(2) f1 = 22.14;
int16 i1 = 7;
f1 + 19.52; //evaluates to 41.66
f1 - (int16 _fixed(2)) i1; //evaluates to 15.14

The increment and decrement operations function the same in binary. This equates to adding or subtracting 1 times the scaling unit.

int16 _fixed(3) f1= 5.234;
f1++; //f1 = 5.235

Casting a fixed type to an integer will truncate the decimal places. This can also be used to isolate the digits after the decimal by subtracting the integer cast from the original

int16 _fixed(2) f1 = 6.94;
int16 noDec = (int16) f1; //noDec = 6
int16 _fixed(2) decOnly = f1 - noDec; //decOnly = 0.94

The CCS C Compiler also supports using printf, sprintf, etc. with the fixed-point type using the "%w" format flag. It will print the value with no leading zeroes and digits after the decimal equal to the precision.

Code:
int16 _fixed(2) f1 = 1.5;
int16 _fixed(2) f2 = 22.78;
int16 _fixed(3) f3 = 5.21
printf("%w - %w - %w", f1, f2, f3);
Output:
1.50 - 22.78 - 5.210

Floating-point numbers have an inescapable error when representing decimal in which expressions that should evaluate to be equal will be off at a very low decimal point. This happens because some decimal numbers in base10, such as 0.1, cannot be perfectly represented in base2, thus causing a rounding error. This is similar to how 1/3 cannot be represented in base10 and is then rounded to 0.33... to whatever precision is needed. Since the CCS C Compiler's implementation of fixed-point uses a decimal scalar, there is 100% precision in base10. This makes it perfect for handling money and other values where this precision is necessary.

Fixed-point operations yield significant performance increases over floating-point. In order to quantify this, benchmarks were performed using a PIC18F45K22 and the CCS C Compiler. One of the on-chip timers was used to approximate the amount of time an arithmetic operation took. For both floating-point and 16 bit fixed-point at 2 places accuracy, each operation was timed and averaged 50 times on a spread of values. The average times for each could then be compared to generalize performance.

The benchmarking results are as follows:
  • Add (+): Fixed ~19.6 times faster than float.
  • Subtract (-): Fixed ~19.4 times faster than float.
  • Multiply (*): Float ~2.7 times faster than fixed.
  • Divide (/): Fixed ~3.3 times faster than float.

The only operation that floating-point performs better is multiplication. This is logical since floats are stored in a multiplicative form (significand x baseexponent). However, fixed-point performs better on the other three, particularly on addition and subtraction.

There is also a program memory usage difference between implementing fixed-point and floating-point. The actual number of instructions it takes to implement the arithmetic operations is significantly different for both. The following program was compiled using both options for the PIC18F45K22.

#ifdef USE_FIXED
int16 _fixed(2) a, b, c;
#else
float a, b, c;
#endif

void main() {
a = 2.25;
b = 0.85;
c = a + b;
printf("Add: %w", c);
c = a - b;
printf("Subtract: %w", c);
c = a * b;
printf("Multiply: %w", c);
c = a / b;
printf("Divide: %w", c);
}

The compiled program's memory statistics were as follows.
Fixed Option:
  • 364 instructions
  • 0.84KB ROM usage
  • 2.6% ROM usage

Float Option:
  • 787 instructions
  • 2.03KB ROM usage
  • 6.3% ROM usage

Switching from the fixed implementation to float increased the instruction count by over 400. This may or may not be significant on the PIC18F45K22, depending on program complexity. However, at 32KB of ROM, it is on the higher end in terms of program memory. On the more limiting units in the PIC18 family, this would be an extremely significant amount of space.


Like us on Facebook. Follow us on Twitter.

About CCS:

CCS is a leading worldwide supplier of embedded software development tools that enable companies to develop premium products based on Microchip PIC® MCU and dsPIC® DSC devices. Complete proven tool chains from CCS include a code optimizing C compiler, application specific hardware platforms and software development kits. CCS' products accelerate development of energy saving industrial automation, wireless and wired communication, automotive, medical device and consumer product applications. Established in 1992, CCS is a Microchip Premier 3rd Party Partner. For more information, please visit http://www.ccsinfo.com.

PIC® MCU, MPLAB® IDE, MPLAB® ICD2, MPLAB® ICD3 and dsPIC® are registered trademarks of Microchip Technology Inc. in the U.S. and other countries.