Fixed Point Numbers

If I’ve understood everything right, then a statement like

for type_distance'small use 0.01;

seems essential in order to avoid errors adding up even with binary fixed point numbers.

I’ve put together a working example to reproduce the problem:

So my question is: What value has a binary fixed point type without the statement to use 'small (see above) ?

I believe this message from Jeff should explain it nicely:

From

Fixed point types use binary as their underlying representation, so by default, their 'Small is 1/2^n, which may generate unexpected results :slight_smile:

Best regards,
Fer

There wouldn’t be errors if the delta was a power of two (e.g. for an LPS25H barometric pressure sensor the LSB in the pressure register is 1/4096 hPa).

I’m not sure why one would use fixed point unless there was some external consideration like that.

Even where the delta is a power of two, there’s nothing wrong with stating the small for confirmation.

1 Like

It is a good question why the Small of a fixed-point type doesn’t default to the specified Delta. That was a choice made back in the early days of Ada (e.g. 1980) but in retrospect it seems like an odd choice. I would recommend you pretty much always specify the Small. These days the “aspect specification” syntax makes it pretty easy:
type D is delta XXX range AAA .. BBB with Small => XXX;
Yes, it is a bit of the department of redundancy department…

3 Likes

I’m not sure that will help the OP, who seems to be aware that the small of an ordinary fixed-point type defaults to a power of two. It’s one of those details that many people are unaware of, but that can be important; another is that the predefined operators of a numeric type operate on the base type.

Fixed-point types were primarily for providing fast real operations for real-time systems on processors without hardware floating-point support around 1980. Such types were common on such systems (represented by scaled integers), but were typically re-implemented for each type.

These days, with floating-point hardware common, fixed-point types are primarily useful for interfacing with hardware that uses scaled integers (as in Wright’s post), and for currencies.

Software engineers have seldom insights into what is going on in the hardware industry. A lot of machinery for milling, sawing, drilling, plotting requires fixed point numbers. Errors adding up are quite dangerous there. So there are good reasons for fixed point numbers.
I’ve studied a lot of books on Ada programming but it seems I’m the first who is asking the question (see initial post).

2 Likes

Also, we have decimal point types, which may be better depending on the application than fixed point types. Just saying :slight_smile:

Well using Fixed-point can solve a technical issue of not having floating-point hardware, it is not the only reason for using it.

Fixed-point and Floating-point have different Pros/Cons. In many (all?) countries, it is illegal to use floating-point for representing money in software that deals with real money. It is recommended to avoid using floating-point for representing time in games.

So, what’s wrong with using floating-point for everything?

As the floating-point … floats around, the size of the step between adjacent values increases or decreases. And that is not acceptable in some use cases. Imagine a bank that used floating-point variables to represent money.

type money is digit 6; -- This won't actually cause the problem shown because the compiler is free to use more precision that this. This is only a minimum.

If you have a balance of $3,000.00 and you deposit your paycheck of $1,560.32 then your new balance would be $4,560.32. Everything is fine so far.
But what if you are saving up for a down payment on a house, so you are not spending your money as fast as you can. :grin:
Now you have a balance of $10,000.00 and you deposit your paycheck of $1,560.32. Now you have 11,560.30! What happen to the 2 cents?!?!

Lets look at how it is represented as a float of 6 digits:

  1.00000 x 10^4  | 10,000.0
+ 1.56032 x 10^3  |  1,560.32
----------------- | ---------
= 1.15603 x 10^4  | 11,560.3

Well, you ran out of precision and the cents dropped off.
If you use a fixed point with a step of 0.01 then this would never happen.

type money is delta 0.01 range -9_999.99 .. 9_999.99 with Small => 0.01;

Now our precision is fixed to the penny. This limits our range but we won’t quietly drop your money.

There is a similar problem with games that use a float to store the game time. Often games intend to track time down to fractions of a second. If the game only lasts a few hours it doesn’t really mater which is used to store the time. But if the game runs for weeks or month or even years, weird problems may crop up in the game because the programmers assumed a game time resolution in some fraction of seconds but the precision of the timer has been pushed out of the fractions of seconds up to the seconds or tens of seconds.

1 Like

I seem to remember the same is true of the Patriot missile system.

Hardly. I saw a program like this in 1984 when I was learning Ada

with Text_IO;
procedure Fixed_Problem is
   type Money is delta 0.01;
   Value : Money := 0.0;
begin
   for I in 1 .. 5 loop
      Value := Value + 0.01;
   end loop;
   Text_IO.Put_Line (Item => Money'Image (Value) );
end Fixed_Problem;

which output 0.04. The instructor presented it to demonstrate this issue, along with the fix of adding

for Money'Small use 0.01;

Perhaps Ada teaching materials don’t usually mention this. Barne’s Programming in Ada 2012 says “The implemented values of a fixed-point type are multiples of a positive real number small. The actual value of small chosen by the implementation can be any power of 2 less than or equal to D [the delta].”, while Clark’s Programming in Ada®: A First Course (1985) doesn’t mention fixed-point types at all. Booch’s Software Engineering with Ada® (second edition, 1987) doesn’t mention that the small defaults to a power of 2.

I guess I didn’t express myself well. One of the HOL project’s main targets in the late 1970s was embedded, real-time software. One of the primary reasons that Ada included fixed-point types was their common use in such software.

@JC001 I was just looking up the Ada 83 rational for this. There is consideration for limitations of the hardware given in the rational, similar to what you said.

See 5.1.2 here:

1 Like

Out of curiosity, does anyone have any input on how-fixed point types are actually implemented (i.e. software or mapped directly to hardware)? I’m wondering if there’s any performance penalty for using these.

If I were to guess I’d say these are implemented in software? I’m thinking, for the IEE754 hardware types the gap increases as the exponent increases (in the normalised range) but remains fixed within a specific exponent so in order to achieve fixed gaps something has to give? Or would the compiler simply reject the type if that is not feasible in hardware (i.e. large numbers, small 'Small’s)

If I correctly remember IBM had decimal packed arithmetic supported by the hardware. You could possibly micro-code PDP to extend its instruction set.

As for implementation of binary fixed-point arithmetic using integers it is straightforward, because the representation X = I * F. Addition and subtraction remain as is. Truncating multiplication requires extended multiplication plus a reduction step (shift).

1 Like

At first, thank you all for joining the discussion.
JC001, you posted an example where the problem was already known back in 1984. So why has the user type the important

for Money'Small use 0.01;

statement at all ? Why is it not default ?

Because the default is the most efficient and correct way to implement it.

The program provided by the malicious :grinning: instructor is semantically wrong. You want a decimal fixed-point number for the currency computations. The program has a binary one, you would rather use in measurements. See RM 3.5.9.

The correct type declaration would be:

type Money is delta 0.01 digits 5; -- Decimal, 5 digits, not for you Elon!

This one would work as expected and have 0.01 small.

Forcing a power of 10 small on a binary fixed-point number is equivalent to having a decimal one, which the language has out of the box (and would map onto a machine type on IBM 370). Software decimal fixed-point has a little more overhead because reduction would require a division rather than a mere shift.

The ’83 Rationale quoted above seem to be 90% of the way there: fixed-point numbers are integer numbers with a scaling factor applied, what difference does it make if the scale is 0.1 or 0.0625? If you’re converting for display, none I think.

There is a difference if you’re adding two numbers, one with a small of 0.1, the other with a small of 0.0625. But in that case you as the programmer have to decide what scaling to perform with the appropriate type conversion, it’s not left up to the compiler.

Dmitry, I don’t think an instructor failing to use a decimal fixed point declaration in 1984 could be called malicious.

1 Like

Let you multiply two binary fixed-point numbers on a binary machine. 0.0625 = 1/32 = 1/2**5 is a power of 2.

   0.9375 * 1.875 =
 = 30 / 2**5 + 60 / 2**5 =
 = 180 / 2**5 / 2**5 =
 = 5 / 2**5 = 0.15625

Multiplication requires shift by 5 bits.

Shift_Right (X * Y, 5)

The scale 0.1 is not a power of two and you have to divide by 10. This is a big difference.

Decimal fixed-point can be mapped onto the hardware like this. Some machines had decimal arithmetic. If I correctly remember VAX-11 had decimal strings so DEC Ada 83 complier could use them for decimal fixed-point. Division by 10 would be a simple shift/slicing. Did DEC Ada that? I do not know.

I was a joke. In 80s we were allowed to do a lot of things forbidden now. [This is a half-joke :grinning:]

Fixed-point types are typically implemented as an integer. The value is the integer multiplied by the small. This is why the Money example gives the wrong answer: Value ends up storing 5, which multiplied by 1/128 gives 0.0390625. Rounding to the nearest multiple of the delta results in the output of 0.04.

1 Like

Don’t ask me. I think it was a mistake.

1 Like