AI22-0057-1

!standard A.5.4(4)                                      23-06-29  AI22-0057-1/03

!class Amendment 23-01-11

!status Corrigendum 1-2022  23-06-29

!status ARG Approved  9-0-0  23-06-11

!status work item 23-01-11

!status received 23-01-11

!priority Low

!difficulty Easy

!subject Floor and other rounding attributes for fixed point types

!summary

Floor, Ceiling, and rounding attributes are supported for fixed point types.

!issue

There is a growing interest in fixed-point with the emergence of machine learning and GPUs. It is nice if an algorithm can be moved between fixed and float without significant re-engineering. However, the attributes Floor, Ceiling, Rounding, Unbiased_Rounding, Machine_Rounding, and Truncation are missing for fixed point types. This forces unnecessary conversions to floating point types, which can themselves introduce rounding effects.

!recommendation

(See Summary.)

!wording

Add after A.5.4(4):

A fixed point type has integral values if its Small value is an integer or the reciprocal of an integer. A generic formal type is defined to have integral values.

[Author’s note: This wording echoes that of G.2.3(21/5); that wording dates to Ada 95.]

The following primitive function attributes are defined for any subtype S of a fixed point type T that has integral values:

S'Ceiling

S'Ceiling denotes a function with the following specification:

function S'Ceiling (X : T)
  return T

 

The function yields the value Ceiling(X), that is, the smallest (most negative) integral value greater than or equal to X.

S'Floor

S'Floor denotes a function with the following specification:

function S'Floor (X : T)
  return T

The function yields the value Floor(X), that is, the largest (most positive) integral value less than or equal to X.

S'Rounding

S'Rounding denotes a function with the following specification:

function S'Rounding (X : T)
  return T

 

The function yields the integral value nearest to X, rounding away from zero if X lies exactly halfway between two integers.

S'Unbiased_Rounding

S'Unbiased_Rounding denotes a function with the following specification:

function S'Unbiased_Rounding (X : T)
  return T

 

The function yields the integral value nearest to X, rounding toward the even integer if X lies exactly halfway between two integers.

S'Machine_Rounding

S'Machine_Rounding denotes a function with the following specification:

function S'Machine_Rounding (X : T)
  return T

 

The function yields the integral value nearest to X. If X lies exactly halfway between two integers, one of those integers is returned, but which of them is returned is unspecified. This function provides access to the rounding behavior which is most efficient on the target processor.

AARM Discussion: We leave the rounding unspecified, so that users cannot depend on a particular rounding. This attribute is intended for use in cases where the particular rounding chosen is irrelevant. If there is a need to know which way values halfway between two integers are rounded, one of the other rounding attributes should be used.

S'Truncation

S'Truncation denotes a function with the following specification:

function S'Truncation (X : T)
  return T

 

The function yields the value Ceiling(X) when X is negative, and Floor(X) otherwise.

[Editor's note: The following is not indented like the above; it applies to all of these attribute functions.]

For all of these attributes, if T is a generic formal type, Program_Error is raised if the actual subtype does not have integral values.

AARM To Be Honest: The check that the type of the prefix has integral values is considered a Legality Rule.

AARM Reason: The rules here for generic formal types are classic assume-the-best rules for legality checking; the check is redone in the specification of the instance. We use a runtime check on the actual subtype to avoid assume-the-worst in generic bodies (which would mean the attributes could never be used in a generic body on a formal subtype). For implementations that expand generic bodies, it is always possible to provide a warning about the exception being raised.

!discussion

We only allow these attributes on types that have integral values in order to avoid nonsense results for types that cannot exactly represent integers.

For instance, for a type defined:

type Foobar is delta 0.7 range 0.0 .. 7.0 with Small => 0.7;

Foobar'Floor(1.4) = 0.7 (as 0.7 is the nearest model number to 1.0). But these functions are defined to return "integral values", and it takes a strong imagination to see 0.7 as an integral value.

Worse, in the worst case, this issue can destroy one of the primary purposes of the rounding attributes: to specify how a value gets rounded to an integer value.

type Horrible is delta 2.5 range -10.0 .. 10.0 with Small => 2.5;
Obj : Horrible := 2.5;
A : Integer := Integer(Horrible'Floor(Obj));

 

The purpose of this expression is to ensure that 2.5 rounds down so that A = 2. But here, the result of Horrible'Floor(Obj) would be 2.5, meaning that the default rounding of the integer conversion would be used - which is defined by 4.6(33) as round away from zero. Thus A will be 3, even though the user carefully wrote the expression needed to prevent that.

Note that if the Small is itself an integer, these attributes are identity functions, so these can be allowed.

An alternative to making these attributes illegal in this case would be to define them as returning Universal_Integer rather than the fixed point type itself. However, this would make them different from the corresponding float attributes. Additionally, implementing these attributes exactly as required would be problematic for some types that do not have integral values.

!ACATS test

ACATS B- and C-Tests are needed to check proper definition and operation of the attributes.

!appendix

This AI was promoted from AI12-0362-1 to be reconsidered for post-Ada 2022 work. The !appendix of the original AI has additional motivation and discussion.