comp.lang.ada
 help / color / mirror / Atom feed
* Renaming Fixed Point Mutiplicative operator in Ada 95
@ 1998-05-19  0:00 Stuart Hutchesson
  1998-05-19  0:00 ` Matthew Heaney
  0 siblings, 1 reply; 20+ messages in thread
From: Stuart Hutchesson @ 1998-05-19  0:00 UTC (permalink / raw)



In certain applications (notably safety critical systems) it is good
practise to supply protected maths operators (guarding against overflow
etc).  In these applications the use of floating point is sometimes  
frowned upon also and a common solution is to use fixed point types and
override/rename the mathematical operations.  

The following code fragment compiles on a number of Ada 83 systems
(VAX Ada, XDAda, ActivAda) and on GNAT 3.09 (Ada95)

package Test1 is

   type NewFixed is delta 0.01 range -1000.0 .. +1000.0;
 
   function MultFixed (LEFT : NewFixed; RIGHT : NewFixed) return
NewFixed ;
      
   procedure Test1DoIt;   

  
end Test1;


package body Test1 is
    
  function "*"   (LEFT  : NewFixed      ; RIGHT : NewFixed     ) return
NewFixed      renames Test1.MultFixed ;

  A1, A2, A3 : NewFixed;
  
  function MultFixed (LEFT : NewFixed; RIGHT : NewFixed) return NewFixed
is
  begin
    return 0.0;
  end MultFixed;
    
  procedure Test1DoIt is
  begin
    A1 := A2 * A3;
  end Test1DoIt;
  
end Test1;


However this is thrown out by ObjectAda saying the line A1 := A2*A3 is
ambiguous - it appears to match the context for the universal_fixed
multipication operator as well as the renamed operator.

Anyone know if this this a compiler bug or a correct interpretation 
of the Ada95 LRM??  If it is the latter then it seems to rule Ada95 
out for systems that need to provide protected fixed point maths.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-19  0:00 Renaming Fixed Point Mutiplicative operator in Ada 95 Stuart Hutchesson
@ 1998-05-19  0:00 ` Matthew Heaney
  1998-05-20  0:00   ` Robert Dewar
  0 siblings, 1 reply; 20+ messages in thread
From: Matthew Heaney @ 1998-05-19  0:00 UTC (permalink / raw)



In article <3561F32B.2F0B@innotts.co.uk>, Stuart Hutchesson
<shutch@innotts.co.uk> wrote:

(start of quote)
In certain applications (notably safety critical systems) it is good
practise to supply protected maths operators (guarding against overflow
etc).  In these applications the use of floating point is sometimes  
frowned upon also and a common solution is to use fixed point types and
override/rename the mathematical operations.  
(end of quote)

I agree that you should use fixed point when it makes sense (you have
absolute - not relative - error).  But why would you want to override (or
otherwise hide) the primitive operators of the fixed point type?

Furthermore, how can you?  Any fixed point type can be multiplied by any
other fixed point type, for example

   type T1 is delta 0.1 range 0.0 .. 10.0;

   type T2 is delta 0.2 range 2.0 .. 200.0;

   type T3 is delta 3.0 range 0.0 .. 3000.0;

declare
   O1 : T1 := 1.3;
   O2 : T2 := 3.8;
   O3 : T3 := O1 * O2;
begin

is perfectly legal.  

How can you anticipate all the fixed point types that you'll multiply with
objects of type NewFixed?

And besides, fixed point operations deliver an _exact_ result.  How do you
even get an overflow exception multiplying fixed point numbers?  The result
of a fixed point multiply is of type universal fixed.  Doesn't universal
fixed already have infinate precision?  You mean you need bigger than
infinity to do a multiply?  Solving some graph problems, perhaps?  Or maybe
a tiling problem?

(start of quote)
package Test1 is

   type NewFixed is delta 0.01 range -1000.0 .. +1000.0;
(end of quote)

Why didn't you specify a 'Small for the type?  You realize of course that
type NewFixed does _not_ have a small equal to 0.01 - it's some power of 2
less than 0.01.

(start of quote) 
   function MultFixed (LEFT : NewFixed; RIGHT : NewFixed) return
NewFixed ;
(end of quote)

This is a very misguided thing to do.  Please, do not do this.

(start of quote)
  function "*"   (LEFT  : NewFixed      ; RIGHT : NewFixed     ) return
NewFixed      renames Test1.MultFixed ;

  A1, A2, A3 : NewFixed;
  
  function MultFixed (LEFT : NewFixed; RIGHT : NewFixed) return NewFixed
is
  begin
    return 0.0;
  end MultFixed;
    
  procedure Test1DoIt is
  begin
    A1 := A2 * A3;
  end Test1DoIt;
  
end Test1;


However this is thrown out by ObjectAda saying the line A1 := A2*A3 is
ambiguous - it appears to match the context for the universal_fixed
multipication operator as well as the renamed operator.
(end of quote)

That's because both operations are directly visible: you're renaming
declaring, and the primitive operation for the type.  Just like the
compiler is telling you.

(start of quote)
Anyone know if this this a compiler bug or a correct interpretation 
of the Ada95 LRM??  If it is the latter then it seems to rule Ada95 
out for systems that need to provide protected fixed point maths.
(end of quote)

I think you need to learn more about fixed point math.

Please, don't override primitive operations of a fixed point type.

No, you don't need "protected" fixed point math.  The features you want are
already built into Ada's fixed point model.   See RM95 4.5.5.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-19  0:00 ` Matthew Heaney
@ 1998-05-20  0:00   ` Robert Dewar
       [not found]     ` <matthew_heaney-ya023680002005981908570001@news.ni.net>
  0 siblings, 1 reply; 20+ messages in thread
From: Robert Dewar @ 1998-05-20  0:00 UTC (permalink / raw)



Matthew says

<<(start of quote)
   function MultFixed (LEFT : NewFixed; RIGHT : NewFixed) return
NewFixed ;
(end of quote)

This is a very misguided thing to do.  Please, do not do this.
>>


What is misguided here is Matthew's entirely odd view about defining
your own operators for fixed-point. Of *course* you may want to do this
for many good reasons, one of which was alluded to in the original
question (another good reason would be to provide modular fixed-point).

The fact that Ada 95 has introduced a nasty incompatibility here is
very irritating. In practice this is one of the really nasty upward
non-compatibilities, and often there is no very simple fix that does
not involve lots of changes to a program.

THe abstraction model of Ada depends on the idea of being able to
override the predefined stuff in a uniform manner. The fact that
you can easily and clearly define "*" on all types except fixed-point
types is definitely a non-orthogonal irritation.

Of course this should not for a moment suggest the extreme reaction of
the original poster, who seems to think that this is a bar to the use
of fixed-point. There are a number of perfectly straightforward alternative
ways to solve the problem. The most irritating case is existing legacy
code, since of course the quoted original program was legal in Ada 83.





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00         ` Robert Dewar
@ 1998-05-21  0:00           ` Simon Pilgrim
  1998-05-21  0:00             ` Matthew Heaney
  1998-05-22  0:00           ` Stuart Hutchesson
  1998-05-22  0:00           ` Rod Chapman
  2 siblings, 1 reply; 20+ messages in thread
From: Simon Pilgrim @ 1998-05-21  0:00 UTC (permalink / raw)



Robert Dewar wrote:
<< Incidentally, in a critical system, I would jolly well hope that
EITHER

  a) you prove that division by zero cannot happen

 or

  b) you test for it explicitly
 
     if z = 0 then
         ....
     else
         a := y /z;
     end if;           >> 

It is not sufficient to just check for equality to zero with fixed point
types.  If z is small enough then a divide by zero exception will still
be generated by some targets (Intel 486 & Motorola 68k for example).

Regards,

Simon Pilgrim




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00           ` Simon Pilgrim
@ 1998-05-21  0:00             ` Matthew Heaney
  1998-05-22  0:00               ` Robert I. Eachus
  0 siblings, 1 reply; 20+ messages in thread
From: Matthew Heaney @ 1998-05-21  0:00 UTC (permalink / raw)



In article <3564A1ED.3DAC@gecm.com>, Simon Pilgrim <simon.pilgrim@gecm.com>
wrote:

(start of quote)
It is not sufficient to just check for equality to zero with fixed point
types.  If z is small enough then a divide by zero exception will still
be generated by some targets (Intel 486 & Motorola 68k for example).
(end of quote)

Are you taking about a floating point divide or a fixed point divide?  They
are very different!

I'm confused by your answer, because a fixed point divide returns universal
fixed. How does one get a divide by zero exception, since 1) we've proven z
is in fact not zero, and 2) the universal fixed result has infinate range?

If you consider that the smallest non-zero value, T'Small, is represented
internally by the value 1, then how does a divide by zero exception occur?




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
       [not found]     ` <matthew_heaney-ya023680002005981908570001@news.ni.net>
@ 1998-05-21  0:00       ` John McCabe
  1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00         ` Matthew Heaney
  1998-05-21  0:00       ` Robert Dewar
       [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
  2 siblings, 2 replies; 20+ messages in thread
From: John McCabe @ 1998-05-21  0:00 UTC (permalink / raw)



matthew_heaney@acm.org (Matthew Heaney) wrote:

>We disagree here - as we have in the past.  Fair enough.  Then how about
>enumerating the specific circumstances under which a fixed point
>multiply can raise an exception? 
>
>My assumption was that, because the multiplication returns universal
>fixed, no exception would be raised.  So what would be the point of
>overriding the predefined operator in order to handle the "exception"?

Are you suggesting that in the example given, if LEFT and RIGHT are both 
100, you get no exception and are returned a valid value (i.e. in the 
range -1000.0 to 1000.0?

If no exception is raised, how do you tell whether the limits of a types 
range have been exceeded or not? 


-- 
Best Regards
John McCabe

=====================================================================
Any opinions expressed are mine and based on my own experience.  They
  should in no way be taken as the opinion of anyone I am currently
     working with, or of the company I am currently working for.
       If you have a problem with anything I say, SPEAK TO ME!
                (remove "nospam." to reply by e-mail)
=====================================================================






^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00       ` John McCabe
@ 1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00         ` Matthew Heaney
  1 sibling, 0 replies; 20+ messages in thread
From: Robert Dewar @ 1998-05-21  0:00 UTC (permalink / raw)



John said

<<Are you suggesting that in the example given, if LEFT and RIGHT are both
100, you get no exception and are returned a valid value (i.e. in the
range -1000.0 to 1000.0?

If no exception is raised, how do you tell whether the limits of a types
range have been exceeded or not?
>>


No, of course he is not suggesting this, he is just being legalistic, if
you write

	x := a * b;

where all variables are the same fixed-point type, then technically and
formally, a * b cannot raise an exception, but it is indeed the case
that the following subtype conversion can raise an exception.

Of course if you write your own operator, this legalistic distinction
disappears, and in any case is completely irrelevant to the discussion!





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
       [not found]     ` <matthew_heaney-ya023680002005981908570001@news.ni.net>
  1998-05-21  0:00       ` John McCabe
@ 1998-05-21  0:00       ` Robert Dewar
  1998-05-22  0:00         ` Robert I. Eachus
       [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
  2 siblings, 1 reply; 20+ messages in thread
From: Robert Dewar @ 1998-05-21  0:00 UTC (permalink / raw)



<<<<We disagree here - as we have in the past.  Fair enough.  Then how about
  enumerating the specific circumstances under which a fixed point multiply
  can raise an exception?

  My assumption was that, because the multiplication returns universal fixed,
  no exception would be raised.  So what would be the point of overriding the
  predefined operator in order to handle the "exception"?>>

>>


It is often useful to override the predefined operators in specialized
circumstances. It is of course the case that a statement like

	a := b * c;

where a,b,c are the same fixed-point type can raise constraint error.
Tecnically the exception comes from the subtype conversion of course.

But you may want, for example,to raise other than CE for a particular
case of fixed-point overflow, or you may want to write operators that
stick at the limits when out of range (such an approach might for
example have saved quite a few ECU since there would be one more
satellite in orbit, and one less embarassing Ariane explosion).

If your advice comes from an attitude of never ever overriding
predefined operators, it is at least consistent, but it is almost
always an indication of lack of perception to declare a feature
in Ada to be in the category of "this should never be used".

The major problem here is that there are indeed many reasons to want
to define your own fixed-point operators. In the case of Ada 83, this
was often done for convenience, precisely to allow

	a := b * c;

which otherwise of course is illegal in Ada 83. 

The incompatible change in Ada 95 is really an annoying incompatibility for
a lot of legacy code. Yes, sometimes it works to simply remove the 
declaration if it is just for the purposes of avoiding the conversions,
but any kind of change like that to big legacy programs is worrisome.

But if the overriding is for some other purpose (here is another application:
make a specialized "*" to count the number of multiplications performed for
performance analysis purposes), then the restriction in this case is
annoying.

Matthew seems to think that somehow this inability to redefine "*" is
a natural consequence of the wonderful power of fixed-point. But that
is clearly nonsense, since we are talking about a minor syntactic
convenience here. The problem does not exist at all in Ada 83, but
one does have to write conversions.

In the Ada 95 design, it seemed quite nice to allow the conversions to
be omitted for an assignment like the above one, and we all agreed, but
at the time *no one* realized that we were introducing a nasty non-upwards
compatibility. I for one (and I suspect others) would have been opposed
to this convenience frill if we had known it would introduce an
incompatibility.

Indeed, I think what we would have discussed would have been the two
following possibilities:

  1. Omit the feature, which adds nothing to expressive power

  2. Include the feature, but add a special preference rule to
	maintain compatibility.

I could have been persuaded to choose option 2, but I would NEVER have
agreed to introducing this incompatibility if I had known about it!

Not only is this an annoying incomtpibility, but it is a huge gotcha!

It means for example that the otherwise perfectly reasonable approach:

define a type x which may be float on some targets fixed on others
define appropriate operators on those types

is trickier than it should be.

Matthew, I am not sure you have much experience with fixed-point, perhaps
it is limited to the cases where it is used to represent specific real world
quantities (in which case of course the profile foo X foo => foo makes no
sense).

But consider using fixed-point for poor mans floating point on a machine
with no hardware floating-point (this was always one of the anticipated
uses).

Now you would like to be able to write

	a := b * c + e * f;

but of course that is illegal. In Ada 83, you could simply defined your
own "*" and "/" and then write expressions of this kind, but there is
simply no replacement for this in Ada 95 that would also preserve literals.

To explain this further, you can of course encapsulate your fixed-point
type within a record, and then of course you can define your own "*",
but then you lose literals.






^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00       ` John McCabe
  1998-05-21  0:00         ` Robert Dewar
@ 1998-05-21  0:00         ` Matthew Heaney
  1 sibling, 0 replies; 20+ messages in thread
From: Matthew Heaney @ 1998-05-21  0:00 UTC (permalink / raw)



In article <6k0na0$po4@gcsin3.geccs.gecm.com>, John McCabe
<john@assen.demon.co.uk> wrote:

(start of quote)
matthew_heaney@acm.org (Matthew Heaney) wrote:

>We disagree here - as we have in the past.  Fair enough.  Then how about
>enumerating the specific circumstances under which a fixed point
>multiply can raise an exception? 
>
>My assumption was that, because the multiplication returns universal
>fixed, no exception would be raised.  So what would be the point of
>overriding the predefined operator in order to handle the "exception"?

Are you suggesting that in the example given, if LEFT and RIGHT are both 
100, you get no exception and are returned a valid value (i.e. in the 
range -1000.0 to 1000.0?
(end of quote)

That is indeed that case.  It doesn't matter what the limits of the
operands are, because the result of muliply is of type universal fixed,
which is unconstrained.

(start of quote)
If no exception is raised, how do you tell whether the limits of a types 
range have been exceeded or not? 
(end of quote)

The constraint check happens during assignment, during implicit conversion
from universal fixed to the target type.




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
       [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
  1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00         ` Robert Dewar
@ 1998-05-21  0:00         ` Matthew Heaney
  2 siblings, 0 replies; 20+ messages in thread
From: Matthew Heaney @ 1998-05-21  0:00 UTC (permalink / raw)



In article <01bd84c3$47215d60$440029a1@m00rq900>, "Stuart Hutchesson"
<shutch@innotts.co.uk> wrote:

(start of quote)
Multiplication was just an example of the compilation problem  - division
is the REAL killer - how do you cope with divide-by-zero exceptions in
critical system when all you are allowed is the catch-all exception
handler, (and run-time checks switched off)? Terminating with an exception
is not an option when you are 2 miles above the Atlantic!  I would prefer
to know EXACTLY what is going on in as much of the object code produced
than leave it to the whim of the compiler.
(end of quote)

I would have handled it at a higher layer of abstraction - not by
overriding the predefined arithematic operators of a scalar type.  For
example,

type Target is limited private;

procedure Move (T : in out Target) is
begin
    <do some math using fixed point types>
exception
   when Constraint_Error =>
      <handle error>
end Move;

As you state, this may not be a solution for you, as you're trying to
repair legacy code.

(start of quote)
(BTW - as Robert Dewar said - you can override Integer maths..... why not
fixed point?)
(end of quote)

Yes, this is a non-orthogonal feature of the language.  However, I wouldn't
override the operators for an integer type either, and so the
non-orthogonality never appears (to me).

Maybe overriding the predefined operators for a scalar type would solve
some problems, but that doesn't mean more won't appear.  Consider this:

generic
   type Integer_Type is range <>;
package GP is ...;

package GP is

   procedure Op (...) is
       O : Integer_Type;
   begin
...
       O := O + 10;
...


type My_Integer is range ...;

function Predefined_Add (L, R : My_Integer) 
  return My_Integer renames "+";

function "+" (L, R : My_Integer)
   return My_Integer;

package P is new GP (My_Integer);

Now, which addition operation do you think package P uses?

If you think it's going to use your special overridden version, then think
again.  Predefined operators reemerge if they're not explicitly imported. 
So you could fix this by doing

generic
   type Integer_Type is range <>;
   with function "+" (L, R : Integer_Type) return Integer_Type is <>;
package GP ...

And all is well until some maintenance programmer does this

package body GP is
   
   procedure Op (...) is
      O : Integer_Type;
   begin
      ... O := O - 5;

and forgot to import the subtraction operator.  If the type you pass in has
overridden subtraction, then what does Op do exactly?  Who knows?  I am
unable to mentally reason about behavior of this code, because I don't even
know what subtraction means anymore.

The reemergence of predefined operators should strike fear into the hearts
of men.  When I look at an addition or a subtraction, I have to do mental
gymnastics to determine whether this is the correct operator.  Or try to
determine whether the generic will continue to work, if it's using the
predefined operator instead of my overriding one.

So, why not save me the trouble, and not override the predefined operators
of a scalar type?  It should give you great pause, any time you're thinking
of replacing an predefined operator.  How many places in the code is the
type passed to a generic?  Does that generic use the operator, and import
it as a generic formal operation?

Equality is even more pernicious.  Suppose I have a stack, say

generic
   type Stack_Item is private;
   Max_Depth : Positive;
package Stacks is

   type Bounded_Stack is private;

   function "=" (L, R : Bounded_Stack) return Boolean;
...
private

   type Stack_Item_Array is 
      array (Positive range <>) of Stack_Item;

   type Bounded_Stack is
       record
          Items : Stack_Item_Array (1 .. Max_Depth);
          Depth : Natural := 0;
       end record;

end Stacks;

package body Stacks is

   function "=" (L, R : Bounded_Stack) is
   begin
      return L.Items (1 .. L.Depth) = R.Items (1 .. R.Depth);
   end;
...


This looks OK, doesn't it?  It compiles and runs fine.  Until I do this

type My_Integer is range ...;
function "=" (L, R : My_Integer) return Boolean;

package My_Integer_Stacks is new Stacks (My_Integer, 10);

Now equality doesn't work anymore!  That's because predefined equality
reemerged as a result of passing the type to the generic.  So let's fix the
stack package, by importing the operator:

generic
   type Stack_Item is private;
   with function "=" (L, R : Stack_Item) return Boolean is <>;
   Max_Depth : Positive;
package Stacks is ...;

Guess what: the package still doesn't work!  Look carefully at the code. 
See anything wrong?

You probably don't see any problems, and that's precisely the issue. 
Predefined equality has reemerged again, in spite of importing it as a
generic formal, when I declared this array

type STack_ITem_Array is ... of STack_Item;

So when we do an array comparison, as in 

   return L.Items (1 .. L.Depth) = R.Items (1 .. R.Depth);

this is using the predefined equality for STack_Item, in spite of the fact
that that operator was imported as a generic formal.

The solution is to ditch the array comparison, so do an item-by-item
compare manually:

function "=" (L, R : Stack) return Boolean is
begin
   if L.Depth /= R.Depth then
     return False;
   end if;

   for I in 1 .. L.Depth loop
      if L.Items (I) /= R.Items (I) then
         return False;
      end if;
   end loop;
   
   return True;
end "=";

That's why I like to take away equality when I declare the array:

type Stack_Item_Array is ...;

function "=" (L, R : Stack_Item_Array) return Boolean is abstract;

Any array comparison will then be flagged as illegal at compile-time, to
remind the unwary maintenance programmer who "forgot" that predefined
equality re-emerges.

I'm not saying, "Never override predefined operators of a scalar type." 
I'm just saying, do it after you've exhausted all other solutions.  The Ada
philosophy is that what the code does should be obvious by just looking at
the code.  But if you replace predefined arithematic operators, that truism
doesn't apply, and it complicates mental reasoning about program behavior.

Sooner or later, a predefined operator for a type will re-emerge, if you've
overridden it.  It may be that overriding the operator solves a problem
now, but you're just playing Russian Roulette, because later on your code
is going to break for another reason.  The error won't even be directly
visible.  You just have to _know_.

And how many programmers do?  Perhaps you and your staff right now are
fully aware of the perniciousness of re-emergence.  But can you guarantee
that a future developers will be aware of this "feature" of the language?




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
       [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
@ 1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00           ` Simon Pilgrim
                             ` (2 more replies)
  1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00         ` Matthew Heaney
  2 siblings, 3 replies; 20+ messages in thread
From: Robert Dewar @ 1998-05-21  0:00 UTC (permalink / raw)



<<Multiplication was just an example of the compilation problem  - division
is the REAL killer - how do you cope with divide-by-zero exceptions in
critical system when all you are allowed is the catch-all exception
handler, (and run-time checks switched off)? Terminating with an exception
is not an option when you are 2 miles above the Atlantic!  I would prefer
to know EXACTLY what is going on in as much of the object code produced
than leave it to the whim of the compiler.
>>


Incidentally, in a critical system, I would jolly well hope that EITHER

 a) you prove that division by zero cannot happen

or

 b) you test for it explicitly

    if z = 0 then
	....
    else
        a := y /z;
    end if;

Once again, I think this is way overblown. Incidentally, I find that having
the convention be that the compiler generates run time checks but you do
NOT permit exception handlers to be a diabolical one. Sounds like another
Ariane on the way. WHo on earth came up with that brilliant designed-to-
cause-trouble decision!

It is that decision that is causing you heartburn, and not surprisingly
so, rather than anything to do with redefining division!





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
       [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
  1998-05-21  0:00         ` Robert Dewar
@ 1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00         ` Matthew Heaney
  2 siblings, 0 replies; 20+ messages in thread
From: Robert Dewar @ 1998-05-21  0:00 UTC (permalink / raw)



S Hutchesson said

<<Multiplication was just an example of the compilation problem  - division
is the REAL killer - how do you cope with divide-by-zero exceptions in
critical system when all you are allowed is the catch-all exception
handler, (and run-time checks switched off)? Terminating with an exception
is not an option when you are 2 miles above the Atlantic!  I would prefer
to know EXACTLY what is going on in as much of the object code produced
than leave it to the whim of the compiler.
>>

Now wait a moment, that makes no sense at all. All you need to do is:

  begin
     x := y / z;
  exception
     when Constraint_Error =>
       ... anythingf you please
  end;

Furthermore, if you do want to override division, you can always do

    x := Divide (y, z);

So while I agree this is annoying, the implication that it is other than
a minor annoyance is overblown!





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00           ` Simon Pilgrim
  1998-05-22  0:00           ` Stuart Hutchesson
@ 1998-05-22  0:00           ` Rod Chapman
  1998-05-22  0:00             ` John McCabe
  2 siblings, 1 reply; 20+ messages in thread
From: Rod Chapman @ 1998-05-22  0:00 UTC (permalink / raw)



Robert Dewar wrote:

> Incidentally, in a critical system, I would jolly well hope that EITHER
>
>  a) you prove that division by zero cannot happen
>

We've done that (for all exceptions) for several non-trivial SPARK programs.
It's actually a useful exercise too!

Most programs we think are exception-free aren't when we attempt the proofs,
and so
in doing so we learn alot about the program (and Ada semantics :-) ) in
addition to
improving the program itself.

Having done so, we can then _justifiably_ turn off run-time checks in the
generated code, which
gives me a nice warm feeling...
 - Rod Chapman
   Praxis Critical Systems







^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-22  0:00           ` Rod Chapman
@ 1998-05-22  0:00             ` John McCabe
  0 siblings, 0 replies; 20+ messages in thread
From: John McCabe @ 1998-05-22  0:00 UTC (permalink / raw)



Rod Chapman <rod@praxis-cs.co.uk> wrote:
>Robert Dewar wrote:
>
>> Incidentally, in a critical system, I would jolly well hope that EITHER
>>
>>  a) you prove that division by zero cannot happen
>>
>
>We've done that (for all exceptions) for several non-trivial SPARK programs.
>It's actually a useful exercise too!

I performed a similar exercise once but, rather than divide by zero, I 
had to prove that our program would not be affected by data dependencies 
in the floating point unit of a GPS MA31750 MIL-STD-1750A processor that 
would result in the wrong answer. It was quite interesting and saved a 
lot of effort in the long run!


-- 
Best Regards
John McCabe

=====================================================================
Any opinions expressed are mine and based on my own experience.  They
  should in no way be taken as the opinion of anyone I am currently
     working with, or of the company I am currently working for.
       If you have a problem with anything I say, SPEAK TO ME!
=====================================================================






^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00         ` Robert Dewar
  1998-05-21  0:00           ` Simon Pilgrim
@ 1998-05-22  0:00           ` Stuart Hutchesson
  1998-05-22  0:00             ` Matthew Heaney
  1998-05-23  0:00             ` Robert Dewar
  1998-05-22  0:00           ` Rod Chapman
  2 siblings, 2 replies; 20+ messages in thread
From: Stuart Hutchesson @ 1998-05-22  0:00 UTC (permalink / raw)





Robert Dewar <dewar@merv.cs.nyu.edu> wrote in article
<dewar.895785693@merv>...
> 
> 
> Incidentally, in a critical system, I would jolly well hope that EITHER
> 
>  a) you prove that division by zero cannot happen
> 
> or
> 
>  b) you test for it explicitly
> 
>     if z = 0 then
> 	....
>     else
>         a := y /z;
>     end if;
> 
If you are suggesting that all divisions are guarded by a test for
denonimator non-zero - then wouln't the most obvious place for that test be
within the division operator itself! Otherwise you have a massive
review/verification effort to check that all divisions are guarded! If it
is inside the operator then that test is done once.  Also
 there is the question of the code that has to be placed inside the "if"
side of the check.

Plus you have to consider all the other guards that have to be placed
around both multipy and divide operations for overflow of the destination
type etc.  The neatest and most cost-effective solution is to put them
inside an overridden operator.

(BTW - the example you gave in your previous posting of surrounding the
operation with an exception handler is banned by SPARK Ada.....)





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-22  0:00           ` Stuart Hutchesson
@ 1998-05-22  0:00             ` Matthew Heaney
  1998-05-23  0:00             ` Robert Dewar
  1 sibling, 0 replies; 20+ messages in thread
From: Matthew Heaney @ 1998-05-22  0:00 UTC (permalink / raw)



In article <01bd8589$5fa05a00$440029a1@m00rq900>, "Stuart Hutchesson"
<shutch@innotts.co.uk> wrote:

>>  b) you test for it explicitly
>> 
>>     if z = 0 then
>>       ....
>>     else
>>         a := y /z;
>>     end if;
>> 
>If you are suggesting that all divisions are guarded by a test for
>denonimator non-zero - then wouln't the most obvious place for that test be
>within the division operator itself! Otherwise you have a massive
>review/verification effort to check that all divisions are guarded! If it
>is inside the operator then that test is done once.  Also
> there is the question of the code that has to be placed inside the "if"
>side of the check.
>
>Plus you have to consider all the other guards that have to be placed
>around both multipy and divide operations for overflow of the destination
>type etc.  The neatest and most cost-effective solution is to put them
>inside an overridden operator.

Will you settle for "inside an operation"?  How about this

generic
   type LT is delta <>;
   type RT is delta <>;
   type Result_Type is delta <>:
function Generic_Divide 
   (L : LT; R : RT) return Result_Type;

function Generic_Divide ... is
begin
   if R = 0.0 then
      return Result_Type'Last;
   end if;

   declare
      Result : Result_Type'Base := L/R;
   begin
       if Result in Result_Type then
          return Result;
       else
          return Result'Last;
      end if;
   end;
end Generic_Divide;

Then you could do something like

function Div is new Generic_Divide (...);

Z := Div (X, Y);

If this works (I didn't try to compile it or anything), then would it be an
acceptable compromise?  (It was stated earlier that this is a fix to legacy
code, so perhaps not.  But would it work in principle?)




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00             ` Matthew Heaney
@ 1998-05-22  0:00               ` Robert I. Eachus
  0 siblings, 0 replies; 20+ messages in thread
From: Robert I. Eachus @ 1998-05-22  0:00 UTC (permalink / raw)



In article <matthew_heaney-ya023680002105981805050001@news.ni.net> matthew_heaney@acm.org (Matthew Heaney) writes:

  > I'm confused by your answer, because a fixed point divide returns universal
  > fixed. How does one get a divide by zero exception, since 1) we've proven z
  > is in fact not zero, and 2) the universal fixed result has infinite range?

   You won't get a divide by zero, but with fixed point it is possible
to have a divide by a value whose abolute value is less than one, and
where the result overflows.  Explicitly testing for that condition is
possible but painful.  (In what follows, assume that type Fixed is
represented as a signed 32-bit integer.  The general case requires
using additional instantiation tricks which would clutter up the
discussion, especially if your compiler supports 64-bit fixed types
and no 64 bit integer type.)

   First specify the operation:

   generic
     type Fixed is delta <>;
     with procedure Handle_Error;
   function Divide(X,Y: Fixed) return Fixed;

   function Divide(X,Y: Fixed) return Fixed is
     type Integer_32 is range -2**31..2**31-1;
   begin
-- The initial problem has three overflow cases, and one where
-- overflow can't occur:

-- 1) Divisor is zero.

   if Y = 0.0 then Handle_Error;

-- 2) Get rid of Fixed'Small >= 1.0;

   elsif Fixed'Small >= 1.0 then return X/Y;

-- 3) X and Y have the same sign, and X/Y > Fixed'Last
--    Note a special case here: for a full range type where X =
--    Fixed'First, division by -1.0 will cause overflow.
    elsif X > 0.0 and Y > 0.0 then

--    We want to test whether there will be an overflow, without
--    causing the overflow.  Using mathematical not computer
--    arithmetic, X/Y is equivalent to (X * Fixed'Small) / (Y *
--    Fixed'Small) overflow occurs only when this result is strictly
--    greater than Fixed'Last/Fixed'Small.  (There are values greater
--    than Fixed'Last which can legitimately be rounded to Fixed'Last,
--    but they can't be generated by a Fixed * Fixed multiply if 1.0
--    is a model number of the type.)

      if Integer_32(X / Fixed'Small) / Integer_32(Y / Fixed'Small) >
        Integer_32(Fixed'Last / Fixed'Small)
      then Handle_Error;
      else return X/Y;
      end if;

-- Note that dividing by Fixed'Small and converting to integer is a no-op.
-- Yes there is one extra integer divide here, but the divides by
-- Fixed'Small should all be either optimized away or special cased 
-- by the compiler.  If not, replace them with Unchecked_Conversions.

-- You can change the computation above to use an integer multiply:
--     if abs Integer_32(X / Fixed'Small) > 
--       abs (Integer_32(Y / Fixed'Small)) *
--         Integer_32(Fixed'Last / Fixed'Small)
--    then...
-- but now there is a problem with overflow on the multiply.  Since
-- one factor is a constant, you can compute for any type the value
-- where the overflow will occur and just omit the calculation if Y is
-- above that value.  But we also know that, excluding the special
-- case mentioned above if abs Y >= 1.0, overflow can't occur.  Since
-- the special case can't happen below, I'll demonstrate there.

-- 3) X and Y have opposite signs, and X/Y < Fixed'First

    else
      if abs Y < 1.0 and then
          abs Integer_32(X / Fixed'Small) <
            - ( abs (Integer_32(Y / Fixed'Small)) *
                Integer_32(Fixed'First / Fixed'Small))
      
      then Handle_Error;
      else return X/Y;
      end if;
    end if;
  end Divide;

  -- Note that this code is NOT guarenteed to work if 1.0 is not an
  -- integer multiple of Fixed'Small.


--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-21  0:00       ` Robert Dewar
@ 1998-05-22  0:00         ` Robert I. Eachus
  1998-05-23  0:00           ` Robert Dewar
  0 siblings, 1 reply; 20+ messages in thread
From: Robert I. Eachus @ 1998-05-22  0:00 UTC (permalink / raw)



In article <dewar.895754578@merv> dewar@merv.cs.nyu.edu (Robert Dewar) writes:

 > In the Ada 95 design, it seemed quite nice to allow the conversions to
 > be omitted for an assignment like the above one, and we all agreed, but
 > at the time *no one* realized that we were introducing a nasty non-upwards
 > compatibility. I for one (and I suspect others) would have been opposed
 > to this convenience frill if we had known it would introduce an
 > incompatibility.

   I think that I remember a version of the preference rule that got
this wrong--under no circumstances do you want to prefer operators
returning _universal_fixed_--and I thought there was a corrected
version which got this right.  Sigh!

 >    2. Include the feature, but add a special preference rule to
 >	   maintain compatibility.

   I could be sold on fixing this correctly, but I would want to be
sure that the new preference rule didn't do unanticipated harm.

 > To explain this further, you can of course encapsulate your fixed-point
 > type within a record, and then of course you can define your own "*",
 > but then you lose literals.

   Depends on how brave you are.  Paragraph 3.5.6(8) allows an
implementation to provide non-standard real types for just this
purpose.  You could modify GNAT if you felt up to it, or I'm sure if
you ask your compiler vendor nicely, he will give you a quote.  Don't
be surprised if it is pretty high--the testing required could be
fierce, even if the implementation is simple.

   Non-standard integer types are also allowed, see RM 3.5.4(26), and
I keep expecting to see implementations use such a type for
System.Address.
--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-22  0:00         ` Robert I. Eachus
@ 1998-05-23  0:00           ` Robert Dewar
  0 siblings, 0 replies; 20+ messages in thread
From: Robert Dewar @ 1998-05-23  0:00 UTC (permalink / raw)



Robert Eachus said

<<   I think that I remember a version of the preference rule that got
this wrong--under no circumstances do you want to prefer operators
returning _universal_fixed_--and I thought there was a corrected
version which got this right.  Sigh!

 >    2. Include the feature, but add a special preference rule to
 >         maintain compatibility.

   I could be sold on fixing this correctly, but I would want to be
sure that the new preference rule didn't do unanticipated harm.>>


Robert Dewar replies

At no time do I recall such a preference rule being discussed, and a quick
check of minutes and notes of relevant meetings provides no clue to what
you are referring to. As far as I know, this issue was never discussed, 
and no one realized there was a problem. This is certainly true of the
most important discussions in the ISRG. Robert Eachus may claim that he
knew about it all along, but if so, he kept it to himself :-)

Robert Dewar said

 > To explain this further, you can of course encapsulate your fixed-point
 > type within a record, and then of course you can define your own "*",
 > but then you lose literals.

Robert Eachus said

<<   Depends on how brave you are.  Paragraph 3.5.6(8) allows an
  implementation to provide non-standard real types for just this
  purpose.  You could modify GNAT if you felt up to it, or I'm sure if
  you ask your compiler vendor nicely, he will give you a quote.  Don't
  be surprised if it is pretty high--the testing required could be
  fierce, even if the implementation is simple.>>

Robert Dewar replies

What has this to do with anything. We are talking about Ada and what you
can do in it. Not what you can do using someone's non-standard extensions.
You might as well discuss whether you can do this in C++, it would be just
as relevant to the discussion. Note that paragraphs like 3.5.6(8) don't
say much, they are just implementation advice as to good taste. An Ada
compiler vendor is free to provide any extensions they like to the language
provided they can be turned off with a switch. 

Robert Eachus said

<<Non-standard integer types are also allowed, see RM 3.5.4(26), and
  I keep expecting to see implementations use such a type for
  System.Address.>>

Robert Dewar replies

I jolly well hope not! Such a decision would be a very bad idea, and would
explicitly violate the advice in section 13.7(37):

37   Address should be of a private type.


If you are saying that the underlying type could make use of this, I don't
see it, typically addresses are either signed (Transputer) or unsigned
(all other machines) binary integers, which can be perfectly well represented
using standard numeric types.





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Renaming Fixed Point Mutiplicative operator in Ada 95
  1998-05-22  0:00           ` Stuart Hutchesson
  1998-05-22  0:00             ` Matthew Heaney
@ 1998-05-23  0:00             ` Robert Dewar
  1 sibling, 0 replies; 20+ messages in thread
From: Robert Dewar @ 1998-05-23  0:00 UTC (permalink / raw)



<<If you are suggesting that all divisions are guarded by a test for
denonimator non-zero - then wouln't the most obvious place for that test be
within the division operator itself! Otherwise you have a massive
review/verification effort to check that all divisions are guarded! If it
is inside the operator then that test is done once.  Also
 there is the question of the code that has to be placed inside the "if"
side of the check.

(BTW - the example you gave in your previous posting of surrounding the
operation with an exception handler is banned by SPARK Ada.....)
>>

Well certainly the problem is aggravated by this restriction. In general,
the Ada features are carefully organized to work together, and when you
start legislating that certain features not be used, you most certainly
are likely to create rough spots.

In particular, it is in general not feasible to define your own operators
for fixed-point multiplication in the general case anyway. This is because
you would need to define n**3 operators where n is the number of fixed-point
types you have. For a relatively simple COBOL-like program where it is
frequently reasonable to have 50-100 such types, you cannot go defining
a million fixed-point multiplication routines!

In Ada (as opposed to some arbitrarily legislated subset or dialect), the
problem of division by zero being tested by the fixed-point multiplication
is solved, at least if Machine_Overflows is True, which is likely for 
fixed-point (and certainly true in all implementations of which I am aware).
This exception can then be caught with whatever granularity you require.

Note that if you are using SPARK Ada, then you are in a realm where you most
definitely ARE expected to prove that what you do does NOT generate exceptions,
so I would think that runtime checks for division by zero would be quite an
oddity in such an applicatoin anyway. If there is a need for data validation,
it would happen much earlier on.





^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~1998-05-23  0:00 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-05-19  0:00 Renaming Fixed Point Mutiplicative operator in Ada 95 Stuart Hutchesson
1998-05-19  0:00 ` Matthew Heaney
1998-05-20  0:00   ` Robert Dewar
     [not found]     ` <matthew_heaney-ya023680002005981908570001@news.ni.net>
1998-05-21  0:00       ` John McCabe
1998-05-21  0:00         ` Robert Dewar
1998-05-21  0:00         ` Matthew Heaney
1998-05-21  0:00       ` Robert Dewar
1998-05-22  0:00         ` Robert I. Eachus
1998-05-23  0:00           ` Robert Dewar
     [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
1998-05-21  0:00         ` Robert Dewar
1998-05-21  0:00           ` Simon Pilgrim
1998-05-21  0:00             ` Matthew Heaney
1998-05-22  0:00               ` Robert I. Eachus
1998-05-22  0:00           ` Stuart Hutchesson
1998-05-22  0:00             ` Matthew Heaney
1998-05-23  0:00             ` Robert Dewar
1998-05-22  0:00           ` Rod Chapman
1998-05-22  0:00             ` John McCabe
1998-05-21  0:00         ` Robert Dewar
1998-05-21  0:00         ` Matthew Heaney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox