* 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
[parent not found: <matthew_heaney-ya023680002005981908570001@news.ni.net>]
[parent not found: <01bd84c3$47215d60$440029a1@m00rq900>]
* 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 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 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-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-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
* 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 [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 [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] ` <matthew_heaney-ya023680002005981908570001@news.ni.net> [not found] ` <01bd84c3$47215d60$440029a1@m00rq900> @ 1998-05-21 0:00 ` Robert Dewar 1998-05-22 0:00 ` Robert I. Eachus 1998-05-21 0:00 ` John McCabe 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 ` 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 [not found] ` <matthew_heaney-ya023680002005981908570001@news.ni.net> [not found] ` <01bd84c3$47215d60$440029a1@m00rq900> 1998-05-21 0:00 ` Robert Dewar @ 1998-05-21 0:00 ` John McCabe 1998-05-21 0:00 ` Matthew Heaney 1998-05-21 0:00 ` Robert Dewar 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 ` Matthew Heaney 1998-05-21 0:00 ` Robert Dewar 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 1998-05-21 0:00 ` John McCabe 1998-05-21 0:00 ` Matthew Heaney @ 1998-05-21 0:00 ` Robert Dewar 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
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> [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 1998-05-21 0:00 ` Robert Dewar 1998-05-22 0:00 ` Robert I. Eachus 1998-05-23 0:00 ` Robert Dewar 1998-05-21 0:00 ` John McCabe 1998-05-21 0:00 ` Matthew Heaney 1998-05-21 0:00 ` Robert Dewar
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox