Good idea to define an "integer type" so that 7/4*4 = 7?

Sometimes I wish that, for example, 7/4*4 = 7 (and not 7/4 = 4) to align with mathematical thinking. Would it be a good idea to define such a new “integer type”?

Given A := 7 / 4, it’s hard to see how B := A * 4; could yield 7. Or, are you looking at rational numbers? That would work …

I beg to disagree. In integer arithmetics (the Z set), 7/4 = 4.
Fortunately, Ada separates cleanly the Z set and the R set, down to literals. Possibly it is the only language to do it right. Most if not all others do various type promotions (implicit conversions) to some degree.
Just add “.0” to literals if you want to switch to the R set.

(Coincidentally, I have recently cleaned up the part of HAC that parses literals: (scanner/parser) Better names and simplifications · zertovitch/hac@5d32ee4 · GitHub)

The code example below illustrates why I bring this “issue”. I made first a naive way to obtain the median of a sorted array and forgot about possible negative indexes:

with Ada.Text_Io; use Ada.Text_Io;
procedure test1 is
-- x  : constant array(Integer range 1 .. 4) of Float := [1.0, 2.0, 3.0, 4.0]; 
   x  : constant array(Integer range -3 .. 0) of Float := [1.0, 2.0, 3.0, 4.0];
   k  : constant Integer := x'last - x'first;

-- i1 : constant Integer := k/2 + x'first;
   i1 : constant Integer := (x'first + x'last)/2; -- gives wrong result for negative indexes

   i2 : constant Integer := i1 + k mod 2;

   median1 : constant float := 0.5*(x(i1) + x(i2));
begin
   Put_Line("x: " & x'Image);
   Put_Line("i1      : " & i1'Image);
   Put_Line("i2      : " & i2'Image);
   Put_Line("median1 : " & median1'Image);
end test1;
``
Output:

x: 
[ 1.00000E+00,  2.00000E+00,  3.00000E+00,  4.00000E+00]
i1      : -1
i2      :  0
median1 :  3.50000E+00

The result is correct (in terms of integer arithmetics) but not what you hope for: you want exactly what the 'Floor attribute does. So the obvious way is to do a short trip to real numbers:

Integer (Float'Floor (Float (x'first + x'last) * 0.5))

Ummm… Surely I misunderstand you? In true integer arithmetic, division is an unusual operation that produces a tuple as output, the quotient and the remainder; i.e., (1,3).

Sounds as if you want a computer algebra system, also called symbolic algebra. That can be implemented in Ada, but an unfortunate reality is that symbolic arithmetic is far slower and much more memory intensive. Many algorithms are worst-case doubly-exponential.

Would it be a good idea to define such a new “integer type”?

If you really need something like that, why not? but you’re about to go down a rabbit hole that I spent ~25 years of my life studying. :grin: Don’t get me wrong; it’s an awesome :rabbit: :hole:, but it gets complicated much more quickly than you’d expect.

But I’m not sure you really need it, as @zertovich points out.

Yes, according to your reference ( Euclidean division - Wikipedia ) (-7)/3 would be -3, as I understand (q = -3, r = 2). I may try to stay away from your :rabbit: :hole:

This might be a cross-language issue … Given what was written, i think it might be a reference to modular arithmetic over the integers, and to ℤ/4 in particular; but in that context, 7 ≡ 3 (mod 4), another ‘view’ of the Euclidean division operation you describe. So i’d be interested in @zertovitch elaborating on their remark.

1 Like

I was alluding to the pair (quotient, remainder) where you discard the remainder.
Modular arithmetics is yet another funny world where, for instance -1 = 9 (modulo 10).
Ada provides this, by the way: type Mod_10 is mod 10;

Can someone explain me this:

   declare
     i : constant Integer := -2/4;
     x : constant Integer := (10)*(-2)/4;
   begin
     Put_Line("i : " & i'Image);
     Put_Line("x : " & x'Image);
   end;

Output:
i : 0
x : -5

Sorry for my confusion. I thought “/” is “(binding) stronger” than “*” and expected to get x = 0.

The ARM (4.5) has this:

multiplying_operator ::= * | / | mod | rem

For a sequence of operators of the same precedence level, the operators are associated with their operands in textual order from left to right. Parentheses can be used to impose specific associations.

That’s more or less in agreement with mathematical convention, at least as far as multiplication and division are concerned.

2 Likes

The one that I always forget is the and/or relationship in Ada:
logical_operator ::= and | or | xor

In my head I expect and to have a higher precedence level than or but in Ada they seem to be on the same precedence level. GNAT also imposes some additional restrictions I couldn’t find in the ARM yet by forcing the use of parenthesis to clarify precedence between the two (instead of them being just optional)

1 Like

This is implicit in the definition of expression in ARM 4.4. See the example in 4.4(15/2):

(Cold and Sunny) or Warm    -- expression (parentheses are required)
1 Like

Which brings us to a really great, subtle point about Ada: the syntax here was designed to avoid errors by requiring parentheses around mixed and-or boolean-expressions. — When on multi-language projects, it’s EASY to get used to the precedence in that other language and come in and get bit by not switching; forcing parentheses avoids that problem altogether.

1 Like