Reputation: 844
Let me first provide some context in hope it will make the problem clearer: I am receiving byte vector data from Hardware which I wish to operate on. I do not wish to convert the date to larger size due to size and time constraints. I want to allow for intermediate values of a calculation to exceed byte range. This is not a problem with scalars (intermediate values are kept in registers and compiler does not issue a constraint error for intermediate values).
However, When working on user defined operators, it is more tricky. We can promote the result to a larger size, but then the assignment back to the original type will require an explicit conversion (subtype cannot have mixed sizes). For example in the code below line 24 would become Z := To_Point((X + Y) / 2);
It is a solution, but I hope to find one that does not require adding the "To_Point" function.
I looked at the implementation of vectors in Ada.Numerics it is using real values, and does not promote intermediate values for example:
function "+" (Left, Right : Real_Vector) return Real_Vector;
This may lead to constraint error, but more likely it may lead to some loss of accuracy (because of the way real number are represented) comparing to scalar calculation (machine dependent).
1. pragma Ada_2012;
2. with Ada.Text_IO; use Ada.Text_IO;
3.
4. procedure Inter_Value is
5. type Byte is new Integer Range 0..255 with Size => 8;
6. A, B, C : Byte;
7.
8. type Point is array(1..2) of Byte with Convention => C, Size => 2*8;
9. X, Y, Z : Point;
10.
11. function "+" (Left, Right : Point) return Point is (Left (1) + Right (1), Left (2) + Right(2));
12. function "/" (Left : Point; Right : Byte) return Point is (Left (1) / Right, Left (2) / Right);
13.
14. begin
15. Put_Line(C'Size'Image);
16. A := 100;
17. B := 200;
18. C := (A + B) / 2; -- Ok, intermediate value in register
19. Put_Line("C = " & C'Image);
20.
21. Put_Line(X'Size'Image);
22. X := (100, 100);
23. Y := (200, 200);
24. Z := (X + Y) / 2; -- CONSTRAINT_ERROR, intermediate value in Point
25. Put_Line("Z = " & Z(1)'Image & Z(2)'Image);
26. end;
Upvotes: 2
Views: 148
Reputation: 3358
To recap from the comments: the declaration of Byte
is equivalent to
type Byte'Base is new Integer;
subtype Byte is Byte'Base range 0 .. 255 with Size => 8;
The important thing about this is that the predefined operators are defined for Byte'Base
. To get similar behavior for Point
you have to explicitly mimic this:
type Point_Base is array (1..2) of Byte'Base;
function "+" (Left : in Point_Base; Right : in Point_Base) return Point_Base is
(Left (1) + Right (1), Left (2) + Right(2) );
function "/" (Left : in Point_Base; Right : in Byte'Base) return Point_Base is
(Left (1) / Right, Left (2) / Right);
subtype Point is Point_Base with
Dynamic_Predicate => (for all P of Point => P in Byte);
Now (X + Y)
gives a Point_Base
that is passed to "/"
, which also gives a Point_Base
. Then a check is made that the result satisfies the constraints of the subtype of Z
before the assignment.
Upvotes: 4
Reputation: 1641
X+Y should be a Point so each component should stay within the Byte range which is not the case.
Why don't you rewrite the line 24 like this?:
Z := (X/2 + Y/2);
As each X and Y component can't exceed 255, adding half of these always stays in the range.
Upvotes: 1