!standard 4.5.5 13-01-08 AC95-00240/00

!class Amendment 13-01-08

!status received no action 13-01-08

!status received 12-10-11

!subject Generalized fixed/integer multiplication and division

!class Amendment 13-01-08

!status received no action 13-01-08

!status received 12-10-11

!subject Generalized fixed/integer multiplication and division

!summary

!appendix

!topic Generalized fixed/integer multiplication and division !reference 4.5.5 !from Adam Beneschan 12-10-11 !discussion The language defines predefined "*" operators on any fixed-point type T and the standard Integer type (4.5.5(14)): function "*" (Left : T; Right : Integer) return T function "*" (Left : Integer; Right : T) return T function "/" (Left : T; Right : Integer) return T However, the language does not provide a way to multiply a fixed-point type by an integer type other than Standard.Integer. Since some integer types can have values that are not in the range of Standard.Integer, there is no convenient way to accomplish this. I don't want the above functions to be predefined for any other integer type; this will certainly cause compatibility problems (making legal code ambiguous), and I think we've already had enough headaches with predefined fixed-point multiplication and name resolution. Instead, I'd like to propose adding generic packages for this purpose: generic type Fix_Num is delta <>; type Int_Num is range <>; package Ada.Numerics.Generic_Fixed_Integer_Multiply is function "*" (Left : Fix_Num; Right : Int_Num) return Fix_Num with Convention => Intrinsic; function "*" (Left : Int_Num; Right : Fix_Num) return Fix_Num with Convention => Intrinsic; function "/" (Left : Fix_Num; Right : Int_Num) return Fix_Num with Convention => Intrinsic; end Ada.Numerics.Generic_Fixed_Integer_Multiply; generic type Fix_Num is delta <>; type Int_Num is mod <>; package Ada.Numerics.Generic_Fixed_Modular_Multiply is ... generic type Fix_Num is delta <> digits <>; type Int_Num is range <>; package Ada.Numerics.Generic_Decimal_Fixed_Integer_Multiply is ... generic type Fix_Num is delta <> digits <>; type Int_Num is mod <>; package Ada.Numerics.Generic_Decimal_Fixed_Modular_Multiply is ... (I'm not sure about the names. My choices all say "Multiply" even though division is also defined. But I think Ada.Numerics.Generic_Decimal_Fixed_Modular_Multiplication_And_Division would be too much even for those who like long, descriptive identifier names.) I can't say just how useful these packages would be. I did recently run into a case where I needed to multiply a fixed-point number by a Long_Integer; however, in that particular case, I figured out that any Long_Integer that was outside the bounds of Integer had to be an input error anyway, so doing a range check and then converting to Integer was good enough. Still, it seems like a flaw that the language doesn't support this; and it shouldn't be too much of a burden on implementors, since they already have to handle fixed-point * Standard.Integer, although I realize that it may require extra code to handle a 64x64 bit multiply as compared to 64x32 or 64x16. *************************************************************** From: Tucker Taft Sent: Thursday, October 11, 2012 9:10 PM ... > However, the language does not provide a way to multiply a fixed-point > type by an integer type other than Standard.Integer. Since some > integer types can have values that are not in the range of > Standard.Integer, there is no convenient way to accomplish this. ... Why not just define a fixed-point type with delta/small 1.0? This seems to give you all the precision you might need. *************************************************************** From: Adam Beneschan Sent: Friday, October 12, 2012 10:19 AM I think it would work, but it's not as easy to do as it sounds. Say I want a fixed-point type to hold a Long_Integer. How do I declare it? type Long_Fixed is delta 1.0 range Long_Integer'First .. Long_Integer'Last; No good, the bounds have to be real. type Long_Fixed is delta 1.0 range Long_Integer'First * 1.0 .. Long_Integer'Last * 1.0; Still no good, there's no "*" operator for multiplying an integer type by a universal real. (At least GNAT is rejecting this, and so is our compiler.) type Long_Fixed is delta 1.0 range Float(Long_Integer'First) .. Float(Long_Integer'Last); Legal, but wrong depending on exactly what types are used. I think that on many implementations, the number of bits in the largest supported integer (and/or the largest supported fixed-point type) is greater than the number of mantissa bits in the largest supported float, which means you're not going to get an exact answer. I was thinking of mentioning this flaw, that there doesn't seem to be a way in Ada to convert a static universal integer to a static universal real (both of which are exact values) without going through a type conversion to an actual floating-point type (which is not exact). Maybe it would be useful to add a language feature to support this, but there aren't many instances where it would be useful, as far as I know. I guess the only correct answer is type Long_Fixed is delta 1.0 range -2.0**(Long_Integer'Size - 1) .. 2.0**(Long_Integer'Size - 1) - 1.0; assuming that it's correct to use the 'Size of a type here. Or maybe it should be 'Object_Size? :) Anyway, this isn't the only place where the language defines something that works for Standard.Integer but not other integer types, and they suffer from the same problem if you want to use a larger integer type that can't safely be converted to Standard.Integer. However, the other cases I can think of--the index of the String type, and the right side of "**"--don't seem to be cases where there's really a need to use integers outside the bounds of Standard.Integer. The case I ran into was (almost) the first case where it seemed like there might be a need to support larger integers, although in my particular case it turned out not to be really necessary. But I thought I should bring it up anyway, in case someone else does see it as a potential need or as a language inconsistency that just "should" be addressed. *************************************************************** From: Tucker Taft Sent: Friday, October 12, 2012 12:40 PM The following should work: type Long_Fixed is delta 1.0 range Long_Integer'Pos(Long_Integer'First) * 1.0 .. Long_Integer'Pos(Long_Integer'Last) * 1.0; *************************************************************** From: Dmitry A. Kazakov Sent: Friday, October 12, 2012 12:19 PM > I think it would work, but it's not as easy to do as it sounds. Say I > want a fixed-point type to hold a Long_Integer. Is there any specific reason why 4.5.5 uses Integer rather than Universal_Integer? *************************************************************** From: Tucker Taft Sent: Friday, October 12, 2012 2:45 PM This is lost in the historical mists of time. This was defined in Ada 83, and never changed since then. With the current interpretation of universal types as parameters, meaning that any numeric type in the same class can be passed as the actual, this would potentially create some ambiguities in existing code, and hence be incompatible. We already dealt with such ambiguities resulting from another change made in Ada 95, and had to add some extra complexity in Ada 2005/2012 to allow user-defined operators to hide the universal operators. It isn't pretty (see 4.5.5 (19.1-19.4) in the Ada 2012 manual). It could be done, but this is the first time I ever remember this issue being raised. Usually you are multiplying the fixed-point number by some relatively small integer, which is often a literal. ***************************************************************

Questions? Ask the ACAA Technical Agent