Using Scaled Integers Instead of Larger Types

When it comes to representing large values, the long int, float, or double data types are usually chosen for variables and expressions. Similarly, floating-point types are the first choice to represent non-integer values. But the longer the data type, the more code is required to access and manipulate these values. Manipulating floating-point values, in particular, is very costly in terms of code size and execution speed. Virtually all floating-point operations require large library routines to be called. Not only is this for functions like pow() and sqrt(), basic operations like addition or even type conversions within expressions involve library calls. Simply printing a floating-point number using printf() requires a large number of library routines to convert the floating-point value into a printable decimal form.

If you need variables to hold a very large range of values, or to hold a small range of fractional values, then your project might benefit from using smaller integer types to store scaled integer values. The scaling factor relates to how the scaled integer value is to be interpreted. Integer values can be scaled up or down to suit your requirements. Scaled-up integers tradeoff precision for range; scaled-down integers tradeoff range for precision.

For example, if your application can tolerate values with half the precision, then you can double the range of values represented by an integer by using a scaling factor of 2. So a 16-bit unsigned integer type can represent values in the range 0 to 131071, but only every second value can be realised. Similarly, an 8-bit unsigned integer with a scaling factor of 16 could hold every 16th value ranging from 0 up to 4095.

Alternatively, if you need increased precision for a smaller range of values, you can scale down the integer. For example scaling down a 16-bit signed integer by a factor of 16 allows you to achieve greater than 1 decimal point accuracy in the values it represents, but that range of values only extends from -2048 to 2047.9375.

There is typically some computational overhead when working with scaled integers, but unlike floating-point values, there is no separate exponent to deal with, so this overhead is much lower. Addition and subtraction of scaled values can be performed using the regular C integer operators and require no adjustment of the result. Multiplication and division of scaled values adjust the scaling factor of the result, which you must keep track of. Alternatively, you can re-scale the result so that uses the original scaling factor. If you need to obtain the true value represented by a scaled integer, you will need a routine to interpret the scaled value, taking into account the scaling factor. Scaling by factors that have a multiple of two can be efficiently performed by shift operations.