Fixed Point Numbers

In Ada 95 and later, yes, but for some reason Ada 95 wasn’t available in 1984.

2 Likes

Ah, I forgot that digits was 95 addition.

In (most, if not all) modern machines the scale of a fixed-point number is a construct in the mind of the programmer. What you have are plain old integers, and you might choose to interpret the LSB as having value 0.1 (1/10) say, or 0.0625 (1/16). If you multiply one of each together, you’ll get a (double-length) product where the LSB has value 1/160. Most of the pain of fixed-point multiplication is working out how you want to deal with that product; of division, how you want to present the dividend to the machine’s divide hardware to get the result you need.

1 Like

But you cannot multiply values of two different fixed-point types anyway.

But in any case the point stands. If your scale is a power of the machine radix you can implement fixed-point multiplication and division using shifts in that radix.

Oh yes you can. You “merely” have to deal with the scaling of the result.

with Ada.Text_IO; use Ada.Text_IO;
procedure Fixed is
   type Dec is delta 0.1 range 0.0 .. 5.0 with
     Small => 0.1;
   type Fix is delta 0.1 range 0.0 .. 5.0;
   Product_Delta : constant := Dec'Small * Fix'Small;
   type Product is delta Product_Delta range 0.0 .. 25.0 with
     Small => Product_Delta;
   Half_Product_Delta : constant := Product_Delta / 2.0;
   type Short_Product is delta Half_Product_Delta range 0.0 .. 25.0 with
     Small => Half_Product_Delta;
   D : Dec := 0.7;
   F : Fix := 0.7;
begin
   Put_Line (Product'(D * F)'Image);
   Put_Line (Short_Product'(D * F)'Image);
end Fixed;

Both outputs are 0.481 (obviously should be 0.49, but it’s that F’(0.7)).

In the example I gave above, the compiler can generate the Product result by taking the unaltered output of the multiplication. To generate the Half_Product result it would have to shift the output up one place. If you decided to assign the result to a type with a ’Small that wasn’t a binary multiple, say Product_Delta / 3.0, the compiler would have to scale the result to match (in that case, multiplying the multiplication’s output by 3).

1 Like

OK, I get universal_fixed which is a rational number.

Compare code generated for decimal and binary fixed-point:

   type Dec is delta 0.1 digits 2 range 0.0 .. 5.0;
   type Fix is delta 0.1 range 0.0 .. 5.0;

   D : Dec := 0.7;
   F : Fix := 0.7;
begin
   D := D * D;
   F := F * F;

You need a lot more for the decimal one because the machine radix is 2.

It’s still quite common for C programmers on microchips to avoid using the single precision FPU due to the hazards of it’s limited range and multiply and divide larger numbers. There are blogs about Cs fixed point libraries being full of dragons too. In that light then Adas fixed point support is a God send. I hear A.I. is using half precision :laughing: and wonder if that affects A.I. inaccuracies further.

Apologies for this late contribution, I recently
saw this thread reprinted in the Ada User Journal.

In Ada 83, it was a requirement to provide power-of-two 'smalls,
whereas providing for other values of 'small was an option.
Providing the power-of-two scalings which meet Ada’s model
number requirements was straightforward, using shifts.

As Simon Wright has said, you can multiply any two different fixed-point
types: ‘you “merely” have to deal with the scaling of the result’.
Performing both the requested multiply (or divide) operation
and a second operation for the scaling (often not just a shift),
all within Ada’s model number requirements, was diffcult.

Essentially it required triple-length arithmetic at worst, but
hardware usually provides double-length products at best. I’m not
sure if those vendors who chose to implement the optional arbitrary
'small arithmetic in Ada 83 did so correctly, and how slow it was.

This problem was recognised in Ada 95 in two ways.
Firstly “relaxed mode” was introduced.
Secondly, even in “strict mode”, the accuracy requirements
were relaxed (see Ada 95 LRM K.2.3 now Ada 2022 LRM G.2.3).
The default use of power-of-two was retained for compatability.

2 Likes

Great to hear from you Terry. In practice, essentially all Ada compilers now support arbitrary “small”s for fixed-point types, and do the “right thing” using integer arithmetic as appropriate. For a while GNAT was using floating-point arithmetic in some cases, but I believe GNAT only uses integer arithmetic now.