!standard 3.5.6(3) 18-02-21 AC95-00304/00 !class Amendment 18-02-21 !status received no action 18-02-21 !status received 18-01-15 !subject Universal numeric data type !summary !appendix !topic Universal numeric data type !reference Ada 2012 RM3.5.6(3) !from Gustavo A. Hoffmann 18-01-15 !keywords numeric types, root_real !discussion This is a proposal for a new universal numeric data type. This data type abstracts floating-point and fixed-point data types. It could be said that this data type allows for developers to implement generic algorithms by directly accessing the root_real type mentioned in RM3.5.6. The goal of this data type is to be able to define a range without having to specify the actual data type used for operations. This is relevant for algorithms where the numeric range is more important than the precision of the actual type. This is usually the case for digital signal processing (DSP) algorithms, for example. The simplest implementation of universal numeric types in Ada 2012 is the one that selects either a floating-point or fixed-point specification at build time. This approach has been discussed in [1]. The syntax of the universal numeric data type could be as follows: type T is range .. The keyword _abstract_ could be used to make it more explicit: type T is abstract range .. Abstract operations could be defined for it. Also, actual types could be derived from the universal data type. This is an example: -- Universal numeric data type for DSP algorithms type T_Fract is abstract range -1.0 .. 1.0; -- Abstract operator for universal numeric data type function "*" (A, B : T_Fract) return T_Fract is abstract; -- Deriving fixed-point data type from universal numeric data type type Fixed_Fract is new T_Fract and delta 1.0 / 2.0 ** 31 with Size => 32; -- Deriving floating-point data type from universal numeric data type type Float_Fract is new T_Fract and digits 10; Also, the universal numeric data type could be used for generic implementations: generic type Filter_Data_Type is new T_Fract; type Filter_Calc_Type is new T_Fract; package Filters is type Filter_Delay is record Z1 : Filter_Calc_Type := 0.0; Z2 : Filter_Calc_Type := 0.0; end record; function Biquad (D : in out Filter_Delay; X_In : Filter_Data_Type) return Filter_Data_Type; end Filters; package body Filters is function Biquad (D : in out Filter_Delay; X_In : Filter_Data_Type) return Filter_Data_Type is X : Filter_Calc_Type; Y : Filter_Calc_Type; -- Coefficients are 16-bit quantized A0 : constant Filter_Calc_Type := 0.00115966796875; A1 : constant Filter_Calc_Type := 0.0023193359375; A2 : constant Filter_Calc_Type := 0.00115966796875; B1 : constant Filter_Calc_Type := (if Filter_Calc_Type'Is_Floating_Point then -1.8319091796875 else -1.8319091796875 / 2); B2 : constant Filter_Calc_Type := 0.836578369140625; begin X := Filter_Calc_Type (X_In); Y := X * A0 + D.Z1; if Filter_Calc_Type'Is_Floating_Point then D.Z1 := X * A1 + D.Z2 - B1 * Y; else D.Z1 := X * A1 + D.Z2 - (B1 * Y) * 2; end if; D.Z2 := X * A2 - B2 * Y; return Filter_Data_Type (Y); end Biquad; end Filters; [1] http://www.electronicdesign.com/embedded-revolution/assessing-ada-language-audio-applications *************************************************************** From: Randy Brukardt Sent: Friday, January 19, 2018 10:05 PM > This is a proposal for a new universal numeric data type. > This data type abstracts floating-point and fixed-point data types. It > could be said that this data type allows for developers to implement > generic algorithms by directly accessing the root_real type mentioned > in RM3.5.6. You probably should have made clear that this is a real type; it does not include integer operations. Ada, of course, has such a type (universal_real), but since you can't declare anything of this type it doesn't help. > The goal of this data type is to be able to define a range without > having to specify the actual data type used for operations. This is > relevant for algorithms where the numeric range is more important than > the precision of the actual type. This is usually the case for digital > signal processing > (DSP) algorithms, for example. I find this goal unusual. One cannot reason much about types for which one doesn't know the precision or how the operations actually work. And it seems more appropriate for a generic formal type than a first-class type. Certainly if you are going to do any static analysis, that needs to include error analysis, and that requires knowing the precision and whether the operations are floating point or fixed point. Even if the range is the primary goal, one needs to be able to use tools to ensure that your answer is more than just noise (significance cancelation being a real problem for real types). Having types that are designed to prevent such analysis seems like a step in the wrong direction. Specifically, it's clear that your generic example subprogram is assuming a particular range and precision even though you claim the precision isn't important. You don't need 14 digits in the coefficients if the precision doesn't matter. :-) Moreover, if given a type with 20 digit precision, the algorithm would be suboptimal. > The simplest implementation of universal numeric types in Ada > 2012 is the one that selects either a floating-point or fixed-point > specification at build time. This approach has been discussed in [1]. > > The syntax of the universal numeric data type could be as follows: > > type T is range .. > This is the syntax of an integer type declaration. Any syntax chosen has to be unique! > The keyword _abstract_ could be used to make it more explicit: > > type T is abstract range .. > That would be OK, as there is no such thing currently. However, one cannot declare objects of abstract types, and that's not a rule that we would want to change so the language stays consistent. I don't know if you need that capability or not, since you didn't give any realistic examples of use outside of a generic unit. > Abstract operations could be defined for it. Also, actual types could > be derived from the universal data type. This is an example: > > -- Universal numeric data type for DSP algorithms > type T_Fract is abstract range -1.0 .. 1.0; > > -- Abstract operator for universal numeric data type > function "*" (A, B : T_Fract) return T_Fract is abstract; This looks like an attempt to define OOP for numeric types in Ada. There have previously been proposals for that, which have always been rejected because of complexity and the fact that they have to operate differently than tagged types. > -- Deriving fixed-point data type from universal numeric data type > type Fixed_Fract is new T_Fract and delta 1.0 / 2.0 ** 31 > with Size => 32; This ought to be illegal, as there is no "*" matching the specification above declared by this type. (Which is the real problem with this idea: "*" for a fixed point type works very differently than "*" for a floating point type.) > -- Deriving floating-point data type from universal numeric data type > type Float_Fract is new T_Fract and digits 10; The use of derived types outside of tagged types (and the semantics of those are different) is rare. I think 90% of the use is in ACATS tests to prove that implementations do this correctly. Most Ada users have no idea what these lines actually do. Also, current rules require either there to be no inherited operations for a derived untagged type or no representation change. You have both here. There are important reasons for that restriction; it might be possible to lift it but that would certainly surface various other semantic issues in the Standard. (Various rules only work because of 13.1(10) bans representation change.) What I don't understand here is why you would want to be able to declare such a type. There only seems to be one such type in the universe -- universal_real (although we might want to name it something else so the extra operations of universal_real don't muck up visibility). Most Ada users aren't going to want to declare a universal type, and you could always add a range later with a subtype. You don't give any examples of non-generic use in the proposal; most uses I can think of seem to make more sense written in a generic unit. For instance: > Also, the universal numeric data type could be used for generic > implementations: > > generic > type Filter_Data_Type is new T_Fract; > type Filter_Calc_Type is new T_Fract; package Filters is > > type Filter_Delay is > record > Z1 : Filter_Calc_Type := 0.0; > Z2 : Filter_Calc_Type := 0.0; > end record; > > function Biquad (D : in out Filter_Delay; > X_In : Filter_Data_Type) return Filter_Data_Type; > end Filters; > > package body Filters is > > function Biquad (D : in out Filter_Delay; > X_In : Filter_Data_Type) return Filter_Data_Type > is > X : Filter_Calc_Type; > Y : Filter_Calc_Type; > > -- Coefficients are 16-bit quantized > A0 : constant Filter_Calc_Type := 0.00115966796875; > A1 : constant Filter_Calc_Type := 0.0023193359375; > A2 : constant Filter_Calc_Type := 0.00115966796875; > B1 : constant Filter_Calc_Type := > (if Filter_Calc_Type'Is_Floating_Point > then -1.8319091796875 > else -1.8319091796875 / 2); > B2 : constant Filter_Calc_Type := 0.836578369140625; > > begin > X := Filter_Calc_Type (X_In); > Y := X * A0 + D.Z1; > if Filter_Calc_Type'Is_Floating_Point then > D.Z1 := X * A1 + D.Z2 - B1 * Y; > else > D.Z1 := X * A1 + D.Z2 - (B1 * Y) * 2; > end if; > D.Z2 := X * A2 - B2 * Y; > > return Filter_Data_Type (Y); > end Biquad; > > end Filters; It would be better and less disruptive for THIS purpose to propose a generic real type that matched any kind of real type (much like the generic discrete type matches any discrete type). A good syntax idea escapes me, so lets just imagine: type Some_Real is delta or digits <>; This would match any real type. But your example shows the problem with this idea: you also are using a new attribute "Is_Floating_Point" (which you didn't propose) in order to write code that works with either floating point or fixed point. (And decimal fixed point probably would require a third set of rules, should someone instantiate this routine with those.) It's unclear this is better than simply defining separate generics for float and fixed point types. Certainly, it is easier to reason about float and fixed operations individually than it is about either proposal (your universal type or my formal real type). [Your example also seems to assume "+" and "-" operations declared for your type, since they wouldn't exist automatically. It helps to keep examples complete and legal assuming the new features -- although in this case I think it is fairly obvious what is missing.] > [1] > http://www.electronicdesign.com/embedded-revolution/assessing- > ada-language-audio-applications I remember reading that article. Good job! The way you handled the choice of floating point or fixed point types seemed exactly right to me. I recall thinking that it isn't possible to really switch between fixed and float perfectly, in part because the analysis needs are different for the different kinds of types. ***************************************************************