Maciek905 | Dreamstime.com
Code Programming Promo 60b1202890f8f

Dealing with the Absence of an FPU Using Fixed Point in Ada

May 28, 2021
Fixed point can be significantly faster especially if there’s no floating-point unit.

This article is part of the Embedded Software series: Ada for the Embedded C Developer

Many numerical applications typically use floating-point types to compute values. However, in some platforms, a floating-point unit may not be available. Other platforms may have a floating-point unit, but its use in certain numerical algorithms could be prohibitive in terms of performance. For those cases, fixed-point arithmetic can be a good alternative.

The difference between fixed-point and floating-point types might not be so obvious when looking at this code snippet:

-- fixed_definitions.ads
package Fixed_Definitions is
  D : constant := 2.0 ** (-31);
  type Fixed is delta D range -1.0 .. 1.0 - D;
end Fixed_Definitions;

-- show_float_and_fixed_point.adb
with Ada.Text_IO;       use Ada.Text_IO;
with Fixed_Definitions; use Fixed_Definitions;

procedure Show_Float_And_Fixed_Point is
  Float_Value : Float := 0.25;
  Fixed_Value : Fixed := 0.25;
begin
  Float_Value := Float_Value + 0.25;
  Fixed_Value := Fixed_Value + 0.25;

  Put_Line (“Float_Value = “ & Float’Image (Float_Value));
  Put_Line (“Fixed_Value = “ & Fixed’Image (Fixed_Value));
end Show_Float_And_Fixed_Point;
In this example, the application will show the value 0.5 for both Float_Value and Fixed_Value.

The major difference between floating-point and fixed-point types is in the way the values are stored. Values of ordinary fixed-point types are, in effect, scaled integers. The scaling used for ordinary fixed-point types is defined by the type's small (scale factor), which is derived from the specified delta and, by default, is a power of two.

Therefore, ordinary fixed-point types are sometimes called binary fixed-point types. In that sense, ordinary fixed-point types can be thought of being close to the actual representation on the machine. In fact, ordinary fixed-point types make use of the available integer shift instructions, for example.

Another difference between floating-point and fixed-point types is that Ada doesn't provide standard fixed-point types—except for the Duration type, which is used to represent an interval of time in seconds. While the Ada standard specifies floating-point types such as Float and Long_Float, we must declare our own fixed-point types. Note that, in the previous example, we’ve used a fixed-point type named “Fixed:”. This type isn't part of the standard, but it must be declared somewhere in the source code of our application.

The syntax for an ordinary fixed-point type is:

type (type_name) is delta (delta_value) 
  range (lower_bound) .. (upper_bound); 

By default, the compiler will choose a scale factor, or small, that’s a power of 2 no greater than .

For example, we may define a normalized range between -1.0 and 1.0 as the following:

-- normalized_fixed_point_type.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Normalized_Fixed_Point_Type is
  D : constant := 2.0 ** (-31);
  type TQ31 is delta D range -1.0 .. 1.0 - D;
begin
  Put_Line ("TQ31 requires " & Integer’Image (TQ31’Size & " bits");
  Put_Line ("The delta   value of TQ31 is " & 
    TQ31'Image (TQ31'Delta));
  Put_Line ("The minimum value of TQ31 is " &
    TQ31'Image (TQ31'First));
  Put_Line ("The maximum value of TQ31 is " &
    TQ31'Image (TQ31'Last));
end Normalized_Fixed_Point_Type;

The output is:

TQ31 requires  32 bits
The delta   value of TQ31 is 0.0000000005
The minimum value of TQ31 is 0.0000000000
The maximum value of TQ31 is 0.9999999995
In this example, we’re defining a 32-bit fixed-point data type for our normalized range. When running the application, we notice that the upper bound is close to one, but not exactly one. This is a typical effect of fixed-point data types—you can find more details in this discussion about the Q format.

In the case of C, since the language doesn't support fixed-point arithmetic, we need to emulate it using integer types and custom operations via functions. Let's look at this very rudimentary example:

-- main.c
#include (stdio.h)
#include (math.h)

#define SHIFT_FACTOR  32
#define TO_FIXED(x)  ((int) ((x) * pow (2.0, SHIFT_FACTOR - 1)))
#define TO_FLOAT(x)  ((float) ((double)(x) * (double)pow (2.0, -(SHIFT_FACTOR – 1))))

typedef int fixed;

fixed add (fixed a, fixed b)
{
  return a + b;
}

fixed mult (fixed a, fixed b)
{
  return (fixed)(((long)a * (long)b) >> (SIFT_FACTOR – 1 ));
}

void display_fixed(fixed x)
{
  printf("value (integer) = %d\n", x);
  printf("value (float)   = %3.5f\n\n", TO_FLOAT(x));
}

int main(int argc, const char * argv[])
{
  int fixed_value = TO_FIXED(0.25);

  printf("Original value\n");
  display_fixed(fixed_value);

  printf("... + 0.25\n");
  fixed_value = add(fixed_value, TO_FIXED(0.25));
  display_fixed(fixed_value);

  printf("... + 0.5\n");
  fixed_value = add(fixed_value, TO_FIXED(0.5));
  display_fixed(fixed_value);

  return 0;
}

Here, we declare the fixed-point type is fixed based on int and two operations for it: addition (via the add function) and multiplication (via the mult function). Note that while fixed-point addition is quite straightforward, multiplication requires right-shifting to match the correct internal representation. In Ada, since fixed-point operations are part of the language specification, they don't need to be emulated. Therefore, no extra effort is required from the programmer.

Also note that the example above is very rudimentary, so it doesn't take some of the side effects of fixed-point arithmetic into account. In C, you have to manually consider all side effects deriving from fixed-point arithmetic, while in Ada, the compiler takes care of selecting the right operations for you.

Read more from the Embedded Software series: Ada for the Embedded C Developer

About the Author

Fabien Chouteau

Fabien joined AdaCore in 2010 after his engineering degree at the EPITA (Paris). He’s involved in real-time, embedded, and hardware simulation technology. Maker/DIYer in his spare time, his projects include electronics, music, and woodworking.

Sponsored Recommendations

Comments

To join the conversation, and become an exclusive member of Electronic Design, create an account today!