Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

conversions between fixed-point types

40 views
Skip to first unread message

Dirk Herrmann

unread,
Sep 18, 2009, 5:35:31 PM9/18/09
to
Hi,

I am currently trying to learn about Ada's fixed-point types, and there
are some points that I couldn't find any statement about, or which I
simply could not understand.

(Background: At my company there is a lot of code that makes heavy use
of fixed-point calculations in a proprietary language. This language
offers special support for fixed-point arithmetics. I am investigating
whether Ada would be a better choice, and thus the fixed-point handling
is important. Note that the decision to use fixed-point arithmetics is
due to the fact that the target processors do not have a floating point
unit - and this will remain this way for some time.)

The first set of questions I have is about conversions between different
fixed-point types.

I realized that with GNAT for the following example types
type FpA is delta 0.5 range -10.0 .. +10.0;
for FpA'Small use 0.5;
type FpB is delta 0.4 range -10.0 .. +10.0;
for FpB'Small use 0.4;
conversions from FpA to FpB work as follows:

FpA FpB
-1.5 ---> -1.2
-1.0 ---> -0.8
-0.5 ---> -0.4
-0.0 ---> -0.0
+0.5 ---> +0.4
+1.0 ---> +0.8
+1.5 ---> +1.2

That is, the conversion is performed similar to a truncation (always
choosing the closest value towards zero). However, in principle there
are other possibilities how the conversion could be done:

truncation floor ceiling round
-1.5 ---> -1.2 -1.6 -1.2 -1.6
-1.0 ---> -0.8 -1.2 -0.8 -1.2
-0.5 ---> -0.4 -0.8 -0.4 -0.4
0.0 ---> 0.0 0.0 0.0 0.0
+0.5 ---> +0.4 +0.4 +0.8 +0.4
+1.0 ---> +0.8 +0.8 +1.2 +1.2
+1.5 ---> +1.2 +1.2 +1.6 +1.6

First question:

There does not seem to be any definite statement in the reference manual
of Ada 2005 about how exactly the conversion has to be performed. All
of the above (and probably more options) seem to be legal, and GNAT just
chooses truncation. Is that right, or have I already missed something?

I have not even found a statement that a compiler has to be consistent
with respect to the strategy it chooses. It might be possible that for
different conversions within the same program different strategies are
chosen. Regarding GNAT, I could not find any statement in the GNAT
documentation about whether GNAT will do it the same way throughout.

Second question:

Is there any way to control how the conversion is performed? Maybe I
have missed something and the language itself offers some possibility?
Is there, for example, some attribute S'Round(X : some fixed point type)
that rounds X to the nearest value of S? I could not find anything
like that.

Or, if this is not the case, do any libraries exist that provide generic
conversion functions to achieve some or all of the "truncate", "floor",
"ceil" or "round" behaviours? As I mentioned at the top of the mail,
any solutions that would require conversions between fixed-point and
floating-point to implement these conversions would probably not be
acceptable for our systems.

Thanks in advance,

Dirk Herrmann
(email address gladly given on request)

Adam Beneschan

unread,
Sep 18, 2009, 6:42:18 PM9/18/09
to

I think you're right, even if the implementation supports the Numerics
Annex (based on G.2.3(10,24)).

> I have not even found a statement that a compiler has to be consistent
> with respect to the strategy it chooses.  It might be possible that for
> different conversions within the same program different strategies are
> chosen.  Regarding GNAT, I could not find any statement in the GNAT
> documentation about whether GNAT will do it the same way throughout.
>
> Second question:
>
> Is there any way to control how the conversion is performed?  Maybe I
> have missed something and the language itself offers some possibility?
> Is there, for example, some attribute S'Round(X : some fixed point type)
>   that rounds X to the nearest value of S?  I could not find anything
> like that.
>
> Or, if this is not the case, do any libraries exist that provide generic
> conversion functions to achieve some or all of the "truncate", "floor",
> "ceil" or "round" behaviours?  As I mentioned at the top of the mail,
> any solutions that would require conversions between fixed-point and
> floating-point to implement these conversions would probably not be
> acceptable for our systems.

The following seems to work if you want to round:

X : FpA;
Y : FpB;

Y := Integer (X / FpB'Small) * FpB'Small;

The conversion to Integer will round (4.6(33)). I didn't check the
resulting code, but this shouldn't use any floating-point operations.

Unfortunately, I can't think of a good solution if you want to insist
(portably) on truncation, even though GNAT does this by default.
Floor and Ceiling probably require you to write a conditional that
behaves differently depending on whether X is < 0 or > 0, but on most
processors that is probably about as efficient as if support for this
existed in the language and the compiler generated the code itself.

-- Adam

Dirk Herrmann

unread,
Sep 19, 2009, 8:41:49 AM9/19/09
to
Dirk Herrmann wrote:

> I am currently trying to learn about Ada's fixed-point types, and there
> are some points that I couldn't find any statement about, or which I
> simply could not understand.

And, while experimenting and trying out Adam Beneschan's solution for
rounding (thanks for your answer, Alan) I got totally confused because
of the following code (I am using GNAT 4.3.4 with the following
command line options: gnatmake -f -gnatVa -gnata -gnatwadhl.o
-save-temps conversion.adb): (For your convenience, I added the results
as comments within the code.)

with Ada.Text_IO;

procedure Conversion is

package TIO renames Ada.Text_IO;
package FIO is new Ada.Text_IO.Float_IO(Float);

type FpA is delta 0.5 range -10.0 .. +10.0; for FpA'Small use 0.5;
type FpB is delta 0.4 range -10.0 .. +10.0; for FpB'Small use 0.4;

function MakeB(F: in Float) return FpB is
begin
return FpB(F);
end MakeB;

function MakeBFromA(F: in FpA) return FpB is
begin
return FpB(F);
end MakeBFromA;

function MakeBViaA(F: in Float) return FpB is
begin
return FpB(FpA(F));
end MakeBViaA;

begin

FIO.Put(Float(FpA(-1.5))); TIO.Put(" "); --> -1.50000E+00
FIO.Put(Float(FpB(-1.5))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(FpB(Float(-1.5)))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(MakeB(-1.5))); TIO.Put(" "); --> -1.60000E+00
FIO.Put(Float(FpB(FpA(-1.5)))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(FpB(FpA(Float(-1.5))))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(MakeBFromA(FpA(-1.5)))); TIO.Put(" "); --> -1.60000E+00
FIO.Put(Float(MakeBViaA(-1.5))); TIO.Put(" "); --> -1.60000E+00

end Conversion;

Is this confusing behaviour allowed? I will submit a bug report if some
expert confirms it is a GNAT bug.

John B. Matthews

unread,
Sep 19, 2009, 10:48:12 AM9/19/09
to
In article <h92jie$q4g$1...@news.albasani.net>,
Dirk Herrmann <fight...@invalid.invalid> wrote:

> Dirk Herrmann wrote:
>
> > I am currently trying to learn about Ada's fixed-point types, and
> > there are some points that I couldn't find any statement about, or
> > which I simply could not understand.
>
> And, while experimenting and trying out Adam Beneschan's solution for
> rounding (thanks for your answer, Alan) I got totally confused
> because of the following code (I am using GNAT 4.3.4 with the
> following command line options: gnatmake -f -gnatVa -gnata
> -gnatwadhl.o -save-temps conversion.adb):

[...]


> Is this confusing behaviour allowed? I will submit a bug report if
> some expert confirms it is a GNAT bug.

I'm no expert, and I had some trouble following the conversions in your
example. I tried a more direct test, and I don't see anything unusual;
rounding appears consistent. Rounding away from zero is required for
decimal fixed point types; but rounding toward zero is permitted for
ordinary fixed point types such as FpA and FpB.

<code>
with Ada.Text_IO;

procedure Conversion is

package TIO renames Ada.Text_IO;
package FIO is new Ada.Text_IO.Float_IO(Float);

type FpA is delta 0.5 range -10.0 .. +10.0; for FpA'Small use 0.5;
type FpB is delta 0.4 range -10.0 .. +10.0; for FpB'Small use 0.4;

package AIO is new Ada.Text_IO.Fixed_IO(FpA);
package BIO is new Ada.Text_IO.Fixed_IO(FpB);

V : Float := -2.0;

begin

while V < 0.0 loop
FIO.Put(V, 1, 1, 0); TIO.Put(" ");
AIO.Put(FpA(V)); TIO.Put(" ");
BIO.Put(FpB(V)); TIO.New_Line;
V := V + 0.1;
end loop;

end Conversion;
</code>

<console>
$ make run
Darwin: gcc 4.3.4
gnatmake conversion -cargs -O2 -bargs -shared -largs -dead_strip
gcc -c -O2 conversion.adb
gnatbind -shared -x conversion.ali
gnatlink conversion.ali -shared-libgcc -dead_strip
./conversion
-2.0 -2.0 -2.0
-1.9 -2.0 -2.0
-1.8 -2.0 -1.6
-1.7 -1.5 -1.6
-1.6 -1.5 -1.6
-1.5 -1.5 -1.6
-1.4 -1.5 -1.2
-1.3 -1.5 -1.2
-1.2 -1.0 -1.2
-1.1 -1.0 -1.2
-1.0 -1.0 -0.8
-0.9 -1.0 -0.8
-0.8 -1.0 -0.8
-0.7 -0.5 -0.8
-0.6 -0.5 -0.4
-0.5 -0.5 -0.4
-0.4 -0.5 -0.4
-0.3 -0.5 -0.4
-0.2 0.0 0.0
-0.1 0.0 0.0
</console>

--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>

tmo...@acm.org

unread,
Sep 19, 2009, 2:38:17 PM9/19/09
to
> And, while experimenting and trying out Adam Beneschan's solution for
> rounding (thanks for your answer, Alan) I got totally confused because
> of the following code (I am using GNAT 4.3.4 with the following
> command line options: gnatmake -f -gnatVa -gnata -gnatwadhl.o
> -save-temps conversion.adb): (For your convenience, I added the results
> as comments within the code.)

Using rather old versions of two compilers, I get:

Gnat 3.15p Janus 3.1.2a

-1.50000E+00 -1.50000E+00
-1.20000E+00 -1.60000E+00
-1.20000E+00 -1.60000E+00
-1.60000E+00 -1.60000E+00
-1.20000E+00 -1.60000E+00
-1.20000E+00 -1.60000E+00
-1.60000E+00 -1.60000E+00
-1.60000E+00 -1.60000E+00

Dirk Herrmann

unread,
Sep 20, 2009, 4:15:49 AM9/20/09
to
Dirk Herrmann wrote:
>> And, while experimenting and trying out Adam Beneschan's solution for
>> rounding (thanks for your answer, Alan) I got totally confused
>> because of the following code (I am using GNAT 4.3.4 with the
>> following command line options: gnatmake -f -gnatVa -gnata
>> -gnatwadhl.o -save-temps conversion.adb):
> [...]
>> Is this confusing behaviour allowed? I will submit a bug report if
>> some expert confirms it is a GNAT bug.

John B. Matthews wrote:
> I'm no expert, and I had some trouble following the conversions in your

> example. [...]

Sorry, I should have given more details about why I am confused.

In particular I am disturbed by the fact that the following two lines produce
different results with GNAT, as has been confirmed for GNAT 3.15p (thanks,
tmoran):


FIO.Put(Float(FpB(Float(-1.5)))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(MakeB(-1.5))); TIO.Put(" "); --> -1.60000E+00

In the first line, the conversion to FpB is done from a Float value, which is
a direct cast from -1.5. In the second line, the conversion to FpB is also
done from a Float value, but in this case -1.5 is passed as a Float argument
to MakeB. What I don't understand is, why it should make a difference whether
I cast -1.5 to Float and then convert it to FpB compared to the situation
where -1.5 is converted to a Float argument, which is then converted to FpB.

The same question arises for the following three lines:


FIO.Put(Float(FpB(FpA(Float(-1.5))))); TIO.Put(" "); --> -1.20000E+00
FIO.Put(Float(MakeBFromA(FpA(-1.5)))); TIO.Put(" "); --> -1.60000E+00
FIO.Put(Float(MakeBViaA(-1.5))); TIO.Put(" "); --> -1.60000E+00

Again, the difference lies in whether a sequence of casts is used compared to
whether the very same value is passed as an argument.

Thanks for your help,

sjw

unread,
Sep 20, 2009, 4:22:07 AM9/20/09
to
On Sep 18, 10:35 pm, Dirk Herrmann <fight_s...@invalid.invalid> wrote:

> I realized that with GNAT for the following example types
>     type FpA is delta 0.5 range -10.0 .. +10.0;
>     for FpA'Small use 0.5;
>     type FpB is delta 0.4 range -10.0 .. +10.0;
>     for FpB'Small use 0.4;
> conversions from FpA to FpB work as follows:
>
>     FpA       FpB
>     -1.5 ---> -1.2
>     -1.0 ---> -0.8
>     -0.5 ---> -0.4
>     -0.0 ---> -0.0
>     +0.5 ---> +0.4
>     +1.0 ---> +0.8
>     +1.5 ---> +1.2
>
> That is, the conversion is performed similar to a truncation (always
> choosing the closest value towards zero).

I don't see this; GNAT rounds ..

with Ada.Text_IO; use Ada.Text_IO;
procedure Fixed
is


type FpA is delta 0.5 range -10.0 .. +10.0;
for FpA'Small use 0.5;
type FpB is delta 0.4 range -10.0 .. +10.0;
for FpB'Small use 0.4;

F : FpA'Base;
G : FpB'Base;
begin
F := -3.0;
loop
exit when F > 3.0;
Put_Line
(FpA'Image (F)
& " "
& FpB'Image (FpB (F))
& " "
& FpA'Image (FpA (FpB (F))));
F := FpA'Succ (F);
end loop;
New_Line;
G := -2.8;
loop
exit when G > 2.8;
Put_Line
(FpB'Image (G)
& " "
& FpA'Image (FpA (G))
& " "
& FpB'Image (FpB (FpA (G))));
G := FpB'Succ (G);
end loop;
end Fixed;

The output (Mac OS X, gcc-4.3.3 and GNAT-GPL-2009) is below, I've
noted the differences with ?'s:

-3.0 -3.2 -3.0 rounded away from 0
-2.5 -2.4 -2.5
-2.0 -2.0 -2.0
-1.5 -1.6 -1.5 " ?
-1.0 -1.2 -1.0 " ?
-0.5 -0.4 -0.5
0.0 0.0 0.0
0.5 0.4 0.5
1.0 1.2 1.0 " ?
1.5 1.6 1.5 " ?
2.0 2.0 2.0
2.5 2.4 2.5
3.0 3.2 3.0 "

-2.8 -3.0 -3.2 " and final value /= initial value
-2.4 -2.5 -2.4 "
-2.0 -2.0 -2.0
-1.6 -1.5 -1.6
-1.2 -1.0 -1.2
-0.8 -1.0 -1.2 " "
-0.4 -0.5 -0.4 "
0.0 0.0 0.0
0.4 0.5 0.4
0.8 1.0 1.2 " "
1.2 1.0 1.2
1.6 1.5 1.6
2.0 2.0 2.0
2.4 2.5 2.4 "
2.8 3.0 3.2 " "

Robert A Duff

unread,
Sep 20, 2009, 10:22:59 AM9/20/09
to
Dirk Herrmann <fight...@invalid.invalid> writes:

> In particular I am disturbed by the fact that the following two lines
> produce different results with GNAT, as has been confirmed for GNAT
> 3.15p (thanks, tmoran):

I don't know, but it might have something to do with the rules for
evaluating static expressions. See RM-4.9. You also need to look at
G.2.3. I don't remember the exact rules for conversions of fixed
point, but I think they do allow some implementation freedom.
If you don't like that, then write code to make it do what
you want. Or use compatible delta/small values.

Note that the rules for decimal fixed point are different.

By the way, you can say:

for T'Small use T'Delta;

to avoid repeating yourself.

3.15p is very old. It would be a good idea to upgrade to a more recent
version.

- Bob

John B. Matthews

unread,
Sep 20, 2009, 11:18:53 AM9/20/09
to
In article <h94obl$c7h$1...@news.albasani.net>,
Dirk Herrmann <fight...@invalid.invalid> wrote:

> Dirk Herrmann wrote:
> >> And, while experimenting and trying out Adam Beneschan's solution
> >> for rounding (thanks for your answer, Alan) I got totally confused
> >> because of the following code (I am using GNAT 4.3.4 with the
> >> following command line options: gnatmake -f -gnatVa -gnata
> >> -gnatwadhl.o -save-temps conversion.adb):
> > [...]
> >> Is this confusing behaviour allowed? I will submit a bug report if
> >> some expert confirms it is a GNAT bug.
>
> John B. Matthews wrote:
> > I'm no expert, and I had some trouble following the conversions in
> > your example. [...]
>
> Sorry, I should have given more details about why I am confused.

Not at all. My confusion arises from my own poor understanding of how to
apply the rules to the various conversions.

> In particular I am disturbed by the fact that the following two lines produce
> different results with GNAT, as has been confirmed for GNAT 3.15p (thanks,
> tmoran):
> FIO.Put(Float(FpB(Float(-1.5)))); TIO.Put(" "); --> -1.20000E+00
> FIO.Put(Float(MakeB(-1.5))); TIO.Put(" "); --> -1.60000E+00
> In the first line, the conversion to FpB is done from a Float value,
> which is a direct cast from -1.5. In the second line, the conversion
> to FpB is also done from a Float value, but in this case -1.5 is
> passed as a Float argument to MakeB. What I don't understand is, why
> it should make a difference whether I cast -1.5 to Float and then
> convert it to FpB compared to the situation where -1.5 is converted
> to a Float argument, which is then converted to FpB.

I see what you mean. Here's my understanding: The Ada Reference Manual
[1], section 3.6(32) on numeric type conversion refers to section G.2.1
and G.2.3 for floating- and fixed-point arithmetic, respectively. The
value -1.5 is exactly representable as type Float, but not as type FpB.
The conversion to FpB is governed by G.2.3(10), so both -1.2 and -1.6
are in the close result set. Because FpB is an ordinary fixed point
type, rather than a decimal fixed point type, there's no requirement to
prefer one element of the result set over another. The conversion back
to Float is discussed in G.2.1(10). Because small is not a power of
T'Machine_Radix, the result is implementation defined. I'm not sure if
GNAT fully implements Annex G, and I may be overlooking something
obvious.

From another perspective, I'm curious to know why the conversions are
needed.

[1]<http://www.adaic.com/standards/05rm/html/RM-TTL.html>

[...]

Dirk Herrmann

unread,
Sep 20, 2009, 2:55:50 PM9/20/09
to
Robert A Duff wrote:
> Dirk Herrmann <fight...@invalid.invalid> writes:
>
>> In particular I am disturbed by the fact that the following two lines
>> produce different results with GNAT, as has been confirmed for GNAT
>> 3.15p (thanks, tmoran):
>
> I don't know, but it might have something to do with the rules for
> evaluating static expressions. See RM-4.9. You also need to look at
> G.2.3. I don't remember the exact rules for conversions of fixed
> point, but I think they do allow some implementation freedom.

Thanks Bob, I will look at those chapters.

> If you don't like that, then write code to make it do what
> you want.

As I said when I started this thread, I am currently investigating whether Ada
could be a good replacement for the proprietary fixed-point oriented language
used in our company. And, convincing my colleagues would certainly not be
easier with statements like "Ada can be used, but you have to implement all
fixed-point stuff yourself". Even more if you can not even rely on the fact
that a compiler will behave the same way throughout.

> Or use compatible delta/small values.

Fortunately, most (but not all) of our fixed-point code uses powers of two for
small. But, even in that case you have to care about rounding when, for
example, you are converting values from a small of 0.125 to a small of 0.5.

> By the way, you can say:
>
> for T'Small use T'Delta;
>
> to avoid repeating yourself.

I have tried that, especially since the very same recommendation was given in
the Barnes book "Ada 2005". But, GNAT gives an error message. It seems that
the very moment you reference T'Delta the type T gets "frozen" such that the
"use" statement itself freezes T before it can modify it. I don't know if
this is what the authors of the reference manual had intended, but GNAT
interprets it that way.

Thanks a lot for your help,
Dirk

Dirk Herrmann

unread,
Sep 20, 2009, 3:13:40 PM9/20/09
to
John B. Matthews wrote:
> I see what you mean. Here's my understanding: The Ada Reference Manual
> [1], section 3.6(32) on numeric type conversion refers to section G.2.1
> and G.2.3 for floating- and fixed-point arithmetic, respectively. The
> value -1.5 is exactly representable as type Float, but not as type FpB.
> The conversion to FpB is governed by G.2.3(10), so both -1.2 and -1.6
> are in the close result set. Because FpB is an ordinary fixed point
> type, rather than a decimal fixed point type, there's no requirement to
> prefer one element of the result set over another.

Thanks for your detailed analysis. It confirms what I had suspected. The bad
thing seems to be that there is no easy approach to achieve a more specific
behaviour.

> The conversion back
> to Float is discussed in G.2.1(10). Because small is not a power of
> T'Machine_Radix, the result is implementation defined. I'm not sure if
> GNAT fully implements Annex G, and I may be overlooking something
> obvious.

I see. Probably the conversion back to float itself was not the problem, though.

> From another perspective, I'm curious to know why the conversions are
> needed.

As I said, in my company we are using lots of fixed-point arithmetics. Most
of it is in powers of 2, but even then you have to care about rounding when
converting between different fixed-point representations.

However, some data is represented with different resolutions. For example,
when you have some code that is executed cyclically every 100ms and delivers
sensor sample values. Then, a gradient of two sensor samples is
(value2-value1)/dt. Since dt is known to be 100ms, you can save a division
operation if you choose a small of 100ms for dt.

At some point during the calculation, however, you will have to make the
conversion from the world of types with "physical" representations (like,
small of 100ms), to the world of binary representations. This is the moment
when conversions between smalls of 0.5 and 0.4 can be interesting.

Best regards,
Dirk

tmo...@acm.org

unread,
Sep 20, 2009, 4:09:20 PM9/20/09
to
I ran on another old compiler, ObjectAda 7.2.2, and got:

Gnat 3.15p Janus 3.1.2a ObjectAda 7.2.2

-1.50000E+00 -1.50000E+00 -1.50000E+00
-1.20000E+00 -1.60000E+00 -1.60000E+00


-1.20000E+00 -1.60000E+00 -1.60000E+00
-1.60000E+00 -1.60000E+00 -1.60000E+00

-1.20000E+00 -1.60000E+00 -1.60000E+00
-1.20000E+00 -1.60000E+00 -1.60000E+00
-1.60000E+00 -1.60000E+00 -1.20000E+00
-1.60000E+00 -1.60000E+00 -1.20000E+00

These were all Ada 95 compilers and I'm guessing the conformance
testing would have included fixed point handling, which would
suggest that any of the three different results would be legal.

Simon Clubley

unread,
Sep 20, 2009, 4:34:51 PM9/20/09
to
On 2009-09-20, Dirk Herrmann <fight...@invalid.invalid> wrote:
>
> As I said when I started this thread, I am currently investigating whether
> Ada could be a good replacement for the proprietary fixed-point oriented
> language used in our company.
>

Are you allowed to tell us which language this is ?

I'm just curious.

Thanks,

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
Microsoft: Bringing you 1980's technology to a 21st century world

Jeffrey R. Carter

unread,
Sep 21, 2009, 1:24:09 PM9/21/09
to
Dirk Herrmann wrote:
>
> In particular I am disturbed by the fact that the following two lines
> produce different results with GNAT, as has been confirmed for GNAT
> 3.15p (thanks, tmoran):
> FIO.Put(Float(FpB(Float(-1.5)))); TIO.Put(" "); --> -1.20000E+00
> FIO.Put(Float(MakeB(-1.5))); TIO.Put(" "); --> -1.60000E+00
> In the first line, the conversion to FpB is done from a Float value,
> which is a direct cast from -1.5. In the second line, the conversion to
> FpB is also done from a Float value, but in this case -1.5 is passed as
> a Float argument to MakeB. What I don't understand is, why it should
> make a difference whether I cast -1.5 to Float and then convert it to
> FpB compared to the situation where -1.5 is converted to a Float
> argument, which is then converted to FpB.

You are complicating matters with your functions and your conversions to and
from Float. Since IIRC your target H/W doesn't support floating-point, you will
be able to see what happens and formulate your questions better if you deal only
with the fixed-point types and direct type conversions between them:

A : Fpa := -1.5;
B : Fpb := Fpb (A);
...
Ada.Text_IO.Put_Line (Fpb'Image (B) );

What you have called a "direct cast" is a type conversion. Also, it is never
necessary to convert a real numeric literal to a real type. Your 1st line could
be written as

FIO.Put (Float (Fpb (Float'(-1.5) ) ) );

With the apostrophe, this is a "qualified expression" and tells the compiler
that the expression in the parentheses is of type Float. However, I suspect that

FIO.Put (Float (Fpb'(-1.5) ) );

is even better.

--
Jeff Carter
"It's all right, Taggart. Just a man and a horse being hung out there."
Blazing Saddles
34

Dirk Herrmann

unread,
Sep 23, 2009, 4:46:06 PM9/23/09
to
Simon Clubley wrote:
> On 2009-09-20, Dirk Herrmann <fight...@invalid.invalid> wrote:
>> As I said when I started this thread, I am currently investigating whether
>> Ada could be a good replacement for the proprietary fixed-point oriented
>> language used in our company.
>>
>
> Are you allowed to tell us which language this is ?

I'd rather not, sorry Simon: Currently I am doing this investigation
completely in my spare time, I have no assignment to do this analysis and
since our current language is a development of my company itself, I feel that
I have to deal with this in a political way.

Best regards,
Dirk

Dirk Herrmann

unread,
Sep 23, 2009, 4:57:16 PM9/23/09
to
Jeffrey R. Carter wrote:
> You are complicating matters with your functions and your conversions to
> and from Float. Since IIRC your target H/W doesn't support
> floating-point, you will be able to see what happens and formulate your
> questions better if you deal only with the fixed-point types and direct
> type conversions between them:

Thanks, Jeff, for the suggestions, I have tried them out. I'd like to add
that our target H/W does not have a floating point unit, but there certainly
is a floating point library.

> What you have called a "direct cast" is a type conversion. Also, it is
> never necessary to convert a real numeric literal to a real type. Your
> 1st line could be written as
>
> FIO.Put (Float (Fpb (Float'(-1.5) ) ) );
>
> With the apostrophe, this is a "qualified expression" and tells the
> compiler that the expression in the parentheses is of type Float.
> However, I suspect that
>
> FIO.Put (Float (Fpb'(-1.5) ) );
>
> is even better.

FpB'Image(FpB'(-1.5)) evaluates to -1.2
FpB'Image(FpB(FpA'(-1.5))) evaluates to -1.2
FpB'Image(MakeBFromA(FpA'(-1.5))) evaluates to -1.6

Thus, even avoiding all floating point operations, the difference is still
there. It appears to be a question of whether the conversion from FpA to FpB
or the conversion from Float to FpB is computed statically or dynamically.

Best regards,
Dirk

Jeffrey R. Carter

unread,
Sep 23, 2009, 6:28:49 PM9/23/09
to
Dirk Herrmann wrote:
>
> FpB'Image(FpB'(-1.5)) evaluates to -1.2
> FpB'Image(FpB(FpA'(-1.5))) evaluates to -1.2
> FpB'Image(MakeBFromA(FpA'(-1.5))) evaluates to -1.6

There is no need to qualify these literals.

> Thus, even avoiding all floating point operations, the difference is
> still there. It appears to be a question of whether the conversion from
> FpA to FpB or the conversion from Float to FpB is computed statically or
> dynamically.

I presume you mean Fpa rather than Float.

Your last sentence may be what's happening. I don't know if that would be
allowed or a compiler error. Any language lawyers want to comment?

--
Jeff Carter
"Go and boil your bottoms."
Monty Python & the Holy Grail
01

Adam Beneschan

unread,
Sep 23, 2009, 9:05:35 PM9/23/09
to
On Sep 23, 3:28 pm, "Jeffrey R. Carter"

<spam.jrcarter....@spam.acm.org> wrote:
> Dirk Herrmann wrote:
>
> > FpB'Image(FpB'(-1.5))                evaluates to -1.2
> > FpB'Image(FpB(FpA'(-1.5)))           evaluates to -1.2
> > FpB'Image(MakeBFromA(FpA'(-1.5)))    evaluates to -1.6
>
> There is no need to qualify these literals.

In the second example, the qualification does make a difference,
semantically; it's the difference between a type conversion from a
fixed-point type to another fixed-point type, and a type conversion
from a universal_real to a fixed-point type. It doesn't make a
difference in this particular case only because -1.5 is a model number
(machine number?) for an FpA. But there's a definite difference
between FpB(0.7) and
FpB(FpA'(0.7)), if truncation is used instead of rounding.


> > Thus, even avoiding all floating point operations, the difference is
> > still there.  It appears to be a question of whether the conversion from
> > FpA to FpB or the conversion from Float to FpB is computed statically or
> > dynamically.
>
> I presume you mean Fpa rather than Float.
>
> Your last sentence may be what's happening. I don't know if that would be
> allowed or a compiler error. Any language lawyers want to comment?

The answer is curious (and I'm not sure if it's what the language
designers intended). 4.9(38) says, "For a real static expression that
is not part of a larger static expression ... the implementation shall
round or truncate the value (according to the Machine_Rounds attribute
of the expected type) to the nearest machine number of the expected
type...". If FpB'Machine_Rounds is TRUE, this means that the result
will be rounded, both for static expressions and when computed at
runtime. (Also, if a number that is halfway between two machine
numbers is rounded, the result is implementation-defined, but thanks
to AI95-268 there is now Implementation Advice that the rounding of
static expressions should be the same as it would be if computed at
runtime.)

If FpB'Machine_Rounds is FALSE, though, the situation is interesting.
4.9(38) requires that static expressions get *truncated* toward zero,
as I read it. But there's no similar requirement for expressions
computed at runtime. A.5.4(3) defines 'Machine_Rounds for a fixed-
point type as "Yields the value True if rounding is performed on
inexact results of every predefined operation that yields a result of
the type T; yields the value False otherwise." It doesn't say that if
it's FALSE, then inexact results are truncated toward zero; it just
means that it might round or truncate. It could be that some
predefined operations always round and others always truncate, or even
that the same predefined operation on different operands may sometimes
round upward and sometimes truncate (unlikely, but the language seems
to allow it). Under this circumstance, it's possible that the type
conversion could produce different results at runtime than for a
static expression.

So I guess whether this is a GNAT bug or not depends on
FpB'Machine_Rounds. If it's TRUE, the first two expressions are
computed incorrectly, but if it's FALSE, this seemingly anomalous
result is allowed.

Also, it's clear that static expressions need to round or truncate to
a multiple of the 'Small if they're not part of a larger static
expression. But I'm not sure what this should do:

FpA'Image (FpA (FpB' (-1.5)))

Here, FpB' (-1.5) *is* part of a larger static expression, so the rule
in 4.9(38) doesn't apply. But I'm not sure just what does apply---is
the value of FpB'(-1.5) still assumed to be -1.5 (so that the type
conversion to FpA still results in -1.5), or should the type
conversion take place (which could result in -1.0 if truncation is
used for both type conversions), or is the result unspecified by the
language?

-- Adam

Jeffrey R. Carter

unread,
Sep 23, 2009, 11:57:07 PM9/23/09
to
Adam Beneschan wrote:
>
> In the second example, the qualification does make a difference,
> semantically; it's the difference between a type conversion from a
> fixed-point type to another fixed-point type, and a type conversion
> from a universal_real to a fixed-point type.

Oops. I know that. I even remember thinking that. Apparently it never got to my
fingers.

Stuart

unread,
Sep 25, 2009, 4:47:24 AM9/25/09
to
"Adam Beneschan" <ad...@irvine.com> wrote in message
news:c8af7daf-9f00-4dbb...@f18g2000prf.googlegroups.com...

> Dirk Herrmann wrote:
>
> > FpB'Image(FpB'(-1.5)) evaluates to -1.2
> > FpB'Image(FpB(FpA'(-1.5))) evaluates to -1.2
> > FpB'Image(MakeBFromA(FpA'(-1.5))) evaluates to -1.6

> The answer is curious (and I'm not sure if it's what the language
> designers intended). 4.9(38) says, ...
...


> If FpB'Machine_Rounds is FALSE, though, the situation is interesting.
> 4.9(38) requires that static expressions get *truncated* toward zero,
> as I read it. But there's no similar requirement for expressions
> computed at runtime.

Having played with this on GNATPro 6.1.2 (to PC target) it does report
FpB'Machine_Rounds as false. It also rounds values like -1.59999 down to
-1.2 (which is a logical consequence). Technically this is still within the
error bounds declared for FpB (delta 0.4) so it is not "wrong" within the
rules of the language definition. But it is very counter-intuitive and is
the sort of thing I find frustrating.

I also tested the problem on GreenHills AdaMULTI (4.2.3 to PowerPC target);
this gave FpB'Machine_Rounds as true and gave the more intuitive results.

-1.5 -> -1.6
-1.40001 -> -1.6
-1.4 -> -1.2

So although there does look to be a language "gotcha" here, it depends upon
the compiler's choice of 'Machine_Rounds.

--
Stuart

sjw

unread,
Sep 25, 2009, 4:41:15 PM9/25/09
to
On Sep 25, 9:47 am, "Stuart" <stu...@0.0> wrote:
> "Adam Beneschan" <a...@irvine.com> wrote in message

This is all very weird. Can I point you to my earlier post at
http://groups.google.com/group/comp.lang.ada/msg/f79a71b9abe7e287
which (unless I'm deluded) shows GNAT rounding away from zero for
dynamic values even though 'Machine_Rounds is False!! (and I get the
same results on Ubuntu 8.04 with GNAT GPL 2008.

Jeffrey R. Carter

unread,
Sep 25, 2009, 5:58:45 PM9/25/09
to
sjw wrote:
>> "Adam Beneschan" <a...@irvine.com> wrote in message
>> news:c8af7daf-9f00-4dbb...@f18g2000prf.googlegroups.com...
>>
>>> If FpB'Machine_Rounds is FALSE, though, the situation is interesting.
>>> 4.9(38) requires that static expressions get *truncated* toward zero,
>>> as I read it. But there's no similar requirement for expressions
>>> computed at runtime.

> This is all very weird. Can I point you to my earlier post at


> http://groups.google.com/group/comp.lang.ada/msg/f79a71b9abe7e287
> which (unless I'm deluded) shows GNAT rounding away from zero for
> dynamic values even though 'Machine_Rounds is False!! (and I get the
> same results on Ubuntu 8.04 with GNAT GPL 2008.

I think Beneschan's quote about what the ARM requires when 'Machine_Rounds is
False explains this. The compiler is truncating for static expressions as
required, but not for dynamic expressions. Perhaps it's rounding, or perhaps
taking the value furthest from zero, for dynamic expressions. The ARM doesn't
seem to require a specific way of converting for dynamic expressions when
'Machine_Rounds is False.

--
Jeff Carter
"You cheesy lot of second-hand electric donkey-bottom biters."


Monty Python & the Holy Grail

14

Dirk Herrmann

unread,
Sep 26, 2009, 10:31:57 AM9/26/09
to
Adam Beneschan wrote:
> So I guess whether this is a GNAT bug or not depends on
> FpB'Machine_Rounds. If it's TRUE, the first two expressions are
> computed incorrectly, but if it's FALSE, this seemingly anomalous
> result is allowed.

I checked that T'Machine_Rounds is FALSE for FpA and FpB. Thus, according to
your analysis the result should be OK with respect to the RM.

> Also, it's clear that static expressions need to round or truncate to
> a multiple of the 'Small if they're not part of a larger static
> expression. But I'm not sure what this should do:
>
> FpA'Image (FpA (FpB' (-1.5)))
>
> Here, FpB' (-1.5) *is* part of a larger static expression, so the rule
> in 4.9(38) doesn't apply. But I'm not sure just what does apply---is
> the value of FpB'(-1.5) still assumed to be -1.5 (so that the type
> conversion to FpA still results in -1.5), or should the type
> conversion take place (which could result in -1.0 if truncation is
> used for both type conversions), or is the result unspecified by the
> language?

The following statement is made in the annotated RM (4.9(38.d/2)):
{AI95-00100-01} Note that the only machine numbers values of a fixed point
type are the multiples of the small, so a static conversion to a fixed-point
type, or division by an integer, must do truncation to a multiple of small.
It is not correct for the implementation to do all static calculations in
infinite precision.
To me this makes it clear that conversions to a fixed point type within a
larger static expression have to convert the value to a multiple of the small.
What surprises me is that it says "truncation" in contrast to 4.9(38) itself.

--
Dirk

Dirk Herrmann

unread,
Sep 26, 2009, 10:43:22 AM9/26/09
to
Stuart wrote:
> Having played with this on GNATPro 6.1.2 (to PC target) it does report
> FpB'Machine_Rounds as false. It also rounds values like -1.59999 down to
> -1.2 (which is a logical consequence). Technically this is still within the
> error bounds declared for FpB (delta 0.4) so it is not "wrong" within the
> rules of the language definition. But it is very counter-intuitive and is
> the sort of thing I find frustrating.
>
> I also tested the problem on GreenHills AdaMULTI (4.2.3 to PowerPC target);
> this gave FpB'Machine_Rounds as true and gave the more intuitive results.
>
> -1.5 -> -1.6
> -1.40001 -> -1.6
> -1.4 -> -1.2
>
> So although there does look to be a language "gotcha" here, it depends upon
> the compiler's choice of 'Machine_Rounds.

Whom could I contact to figure out whether this is an omission in the language
definition (which could then be fixed) or whether it is intentional? I would
also like to ask about another issue:

Dirk Herrmann wrote:


> Robert A Duff wrote:
>> By the way, you can say:
>>
>> for T'Small use T'Delta;
>>
>> to avoid repeating yourself.
>
> I have tried that, especially since the very same recommendation was
> given in the Barnes book "Ada 2005". But, GNAT gives an error message.
> It seems that the very moment you reference T'Delta the type T gets
> "frozen" such that the "use" statement itself freezes T before it can
> modify it. I don't know if this is what the authors of the reference
> manual had intended, but GNAT interprets it that way.

--
Dirk

Simon Clubley

unread,
Sep 27, 2009, 1:15:15 PM9/27/09
to
On 2009-09-23, Dirk Herrmann <fight...@invalid.invalid> wrote:

> Simon Clubley wrote:
>>
>> Are you allowed to tell us which language this is ?
>
> I'd rather not, sorry Simon: Currently I am doing this investigation
> completely in my spare time, I have no assignment to do this analysis and
> since our current language is a development of my company itself, I feel that
> I have to deal with this in a political way.
>

I understand. (I had not realised it was a language your company had
designed).

Thanks for having taken the time to reply.

sjw

unread,
Sep 27, 2009, 3:22:38 PM9/27/09
to
On Sep 20, 7:55 pm, Dirk Herrmann <fight_s...@invalid.invalid> wrote:

> As I said when I started this thread, I am currently investigating whether Ada
> could be a good replacement for the proprietary fixed-point oriented language
> used in our company.  And, convincing my colleagues would certainly not be
> easier with statements like "Ada can be used, but you have to implement all
> fixed-point stuff yourself".  Even more if you can not even rely on the fact
> that a compiler will behave the same way throughout.

Is the target hardware standard? (eg, PPC without floating-point
support) If not, you're looking at a compiler port as well.

Even though GNAT/x86 seems to truncate on predefined operations and on
conversion from static values (though not, as far as I can tell, on
dynamic conversion between two types!), there seems no intrinsic
reason why a port to different hardware might not be made to round.

> Robert A Duff wrote:

> > By the way, you can say:
>
> >     for T'Small use T'Delta;
>
> > to avoid repeating yourself.
>
> I have tried that, especially since the very same recommendation was given in
> the Barnes book "Ada 2005".  But, GNAT gives an error message.  It seems that
> the very moment you reference T'Delta the type T gets "frozen" such that  the
> "use" statement itself freezes T before it can modify it.  I don't know if
> this is what the authors of the reference manual had intended, but GNAT
> interprets it that way.

The idiom used to the

T_Delta : constant := 0.4;
type T is delta T_Delta range -10.0 .. 10.0;
for T'Small use T_Delta;

Stuart

unread,
Sep 28, 2009, 9:40:32 AM9/28/09
to

"sjw" <simon.j...@mac.com> wrote in message
news:35adcbc7-dec6-45ae...@l35g2000vba.googlegroups.com...

> On Sep 25, 9:47 am, "Stuart" <stu...@0.0> wrote:
> "Adam Beneschan" <a...@irvine.com> wrote in message
..
> > Dirk Herrmann wrote:
DH> FpB'Image(FpB'(-1.5)) evaluates to -1.2
DH> FpB'Image(FpB(FpA'(-1.5))) evaluates to -1.2
DH> FpB'Image(MakeBFromA(FpA'(-1.5))) evaluates to -1.6

AB> The answer is curious (and I'm not sure if it's what the language
AB> designers intended). 4.9(38) says, ...
AB> ...
AB> If FpB'Machine_Rounds is FALSE, though, the situation is interesting.
AB> 4.9(38) requires that static expressions get *truncated* toward zero,
AB> as I read it. But there's no similar requirement for expressions
AB> computed at runtime.

S> Having played with this on GNATPro 6.1.2 (to PC target) it does report
S> FpB'Machine_Rounds as false. It also rounds values like -1.59999 down to
S> -1.2 (which is a logical consequence). Technically this is still within
the
S> error bounds declared for FpB (delta 0.4) so it is not "wrong" within the
S> rules of the language definition. But it is very counter-intuitive and is
S> the sort of thing I find frustrating.
S>
S> I also tested the problem on GreenHills AdaMULTI (4.2.3 to PowerPC
target);
S> this gave FpB'Machine_Rounds as true and gave the more intuitive results.
S>
S> -1.5 -> -1.6
S> -1.40001 -> -1.6
S> -1.4 -> -1.2
S>
S> So although there does look to be a language "gotcha" here, it depends
upon
S> the compiler's choice of 'Machine_Rounds.

SW> This is all very weird. Can I point you to my earlier post at
SW> http://groups.google.com/group/comp.lang.ada/msg/f79a71b9abe7e287
SW> which (unless I'm deluded) shows GNAT rounding away from zero for
SW> dynamic values even though 'Machine_Rounds is False!! (and I get the
SW> same results on Ubuntu 8.04 with GNAT GPL 2008.

Sorry for any confusion Simon, I was stating my results for static values;
for computed/dynamic conversions GNAT does round to nearest (so, away from 0
for -1.5).
--
Stuart


Adam Beneschan

unread,
Sep 28, 2009, 11:15:17 AM9/28/09
to
On Sep 26, 7:43 am, Dirk Herrmann <fight_s...@invalid.invalid> wrote:

> Whom could I contact to figure out whether this is an omission in the language
> definition (which could then be fixed) or whether it is intentional?  I would
> also like to ask about another issue:
>
> Dirk Herrmann wrote:
>
>  > Robert A Duff wrote:
>  >> By the way, you can say:
>  >>
>  >>     for T'Small use T'Delta;
>  >>
>  >> to avoid repeating yourself.
>  >
>  > I have tried that, especially since the very same recommendation was
>  > given in the Barnes book "Ada 2005".  But, GNAT gives an error message.
>  > It seems that the very moment you reference T'Delta the type T gets
>  > "frozen" such that  the "use" statement itself freezes T before it can
>  > modify it.  I don't know if this is what the authors of the reference
>  > manual had intended, but GNAT interprets it that way.

Although at first glance it seems like you should be able to do this,
I don't think it's likely that the RM could make this work in the
general case. The rules do say that the occurrence of T'Delta causes
freezing; I believe this is part of the rule that an expression causes
freezing (except for nonstatic expressions used in parameter or
component defaults and some other cases). Here, T'Delta is a static
expression and thus requires T to be frozen.

If we were to make an exception that, say, a representation attribute
of T does not cause freezing if it occurs in another representation
clause for T, you could end up with some interesting situations:

type Arr is array (1 .. 5) of Boolean;
for A'Size use A'Component_Size * 8;
for A'Component_Size use A'Size - 14;

and now the compiler has to figure out how to solve for two
variables. I suppose it's possible to write RM rules in such a way as
to prevent things like this while allowing the 'Small/'Delta case, but
this either requires some pretty complex rules, or rules that just
spell out a number of special cases (such as saying "An expression
causes freezing except in the one particular clause FOR T'SMALL USE
T'DELTA). Neither one is going to be appealing particularly for such
a small gain---it doesn't give programmers the ability to do anything
that couldn't be accomplished easily with just a tiny bit more
effort.

Bottom line: it isn't possible to make the language make everything
work that looks like it should work. Ada is, after all, a Programming
Language and not a Mind-Reading Machine.

-- Adam

Robert A Duff

unread,
Sep 28, 2009, 2:37:32 PM9/28/09
to
Dirk Herrmann <fight...@invalid.invalid> writes:

> Robert A Duff wrote:
>> Dirk Herrmann <fight...@invalid.invalid> writes:
> As I said when I started this thread, I am currently investigating
> whether Ada could be a good replacement for the proprietary fixed-point
> oriented language used in our company. And, convincing my colleagues
> would certainly not be easier with statements like "Ada can be used, but
> you have to implement all fixed-point stuff yourself".

Well, yeah, but not "all fixed-point stuff" -- just conversions,
when the 'Smalls are incompatible.

Does it make sense in your context to avoid "odd-ball" smalls?
Note that the default 'Small is a power of 2.

>> By the way, you can say:
>> for T'Small use T'Delta;
>> to avoid repeating yourself.
>
> I have tried that, especially since the very same recommendation was
> given in the Barnes book "Ada 2005". But, GNAT gives an error message.

Oops. You're right. It's illegal by the freezing rules.
Sorry for the misinformation.

- Bob

Dirk Herrmann

unread,
Sep 28, 2009, 4:18:50 PM9/28/09
to
sjw wrote:
> Is the target hardware standard?

Yes, it's an ARM core.

> T_Delta : constant := 0.4;
> type T is delta T_Delta range -10.0 .. 10.0;
> for T'Small use T_Delta;

:-)

However, what I don't like about this solution is, that you have to make
T_Delta publically visible. My point is not about anybody not knowing about
T'Small - it's just that you can not control the use of the symbol T_Delta.
Maybe later you want to rename it, or share the same constant between T1 and T2.

--
Dirk

Dirk Herrmann

unread,
Sep 28, 2009, 4:50:35 PM9/28/09
to
Robert A Duff wrote:
> Does it make sense in your context to avoid "odd-ball" smalls?
> Note that the default 'Small is a power of 2.

Actually, most of our code uses powers of two. Still we have to think about
rounding when, for example, we are converting values from a small of 0.125 to
a small of 0.5. But, Adam's suggestion seems to work fine:
Y := Integer (X / FpB'Small) * FpB'Small;
The resulting x86 assembly code could be improved a little, but I have not
checked the ARM code yet.

--
Dirk

0 new messages