Version 1.1 of acs/ac-00240.txt

Unformatted version of acs/ac-00240.txt version 1.1
Other versions for file acs/ac-00240.txt

!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
!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