Integer Types: Signed Ranges, Modular Types & Overflow Checks

Declare your own signed integer types with ranges, use the predefined Integer, Natural and Positive, understand division, rem and mod, modular (unsigned, wrapping) types and bitwise operations, and see how Ada detects overflow instead of wrapping silently.

Integers are the most common data in any program, and Ada lets you say exactly which integers are valid. Instead of choosing from int, long and unsigned, you declare 'type Day_Of_Month is range 1 .. 31;' and the compiler picks a suitable machine representation while every assignment is checked against the range. When you do want wraparound arithmetic — hashing, checksums, hardware registers — you use a modular type. This lesson covers both families and the arithmetic operators.

Signed Integer Types with Ranges

'type T is range Low .. High;' declares a new signed integer type. The bounds must be static (known at compile time). The compiler chooses the underlying size (8, 16, 32 or 64 bits), and each operation that stores a value into T checks that it lies within the range, raising Constraint_Error otherwise. Intermediate arithmetic uses the base type (the underlying machine range, T'Base), so an expression like (A + B) / 2 can exceed T's range temporarily without error as long as the final value fits.

The predefined type Integer is guaranteed at least 16 bits and is 32 bits on GNAT. Standard also provides the subtypes Natural (0 .. Integer'Last) and Positive (1 .. Integer'Last), plus Long_Integer and Long_Long_Integer. Best practice is to declare your own types for domain quantities (counts, indices, identifiers) and reserve Integer for genuinely general-purpose values.

• type T is range L .. H; — new signed integer type with checked range. • Integer (32-bit in GNAT), Natural (≥ 0), Positive (≥ 1), Long_Integer, Long_Long_Integer. • Intermediate results use T'Base; only stored values are range-checked. • Prefer domain-specific types over plain Integer.

Arithmetic: /, rem, mod and **

Integer division / truncates toward zero, so -7 / 2 is -3. Ada has two remainder operators: rem gives a result with the sign of the dividend (-7 rem 2 = -1), and mod gives a result with the sign of the divisor (-7 mod 2 = 1). mod is the one you want for 'clock arithmetic' (wrapping an index around a circular buffer). ** raises to a non-negative integer power, abs gives the absolute value. Division by zero raises Constraint_Error.

• / truncates toward zero: 7 / 2 = 3, -7 / 2 = -3. • rem: sign follows the dividend; mod: sign follows the divisor. • A = (A / B) * B + (A rem B) always holds. • ** exponent must be a non-negative Natural; abs for magnitude. • Division by zero → Constraint_Error.

Overflow Detection

In C, signed overflow is undefined behaviour and usually wraps silently. In Ada, if an integer operation's result does not fit in the base type, Constraint_Error is raised. GNAT enables overflow checks by default in modern versions. This single rule eliminates a whole category of security vulnerabilities (buffer size calculations that wrap to small numbers) and latent logic errors.

• Signed overflow raises Constraint_Error (never silent wraparound). • Checks can be suppressed (pragma Suppress) only deliberately — rarely justified. • SPARK can prove statically that no overflow can occur.

Modular Types

'type Byte is mod 256;' declares an unsigned type whose values are 0 .. 255 and whose arithmetic wraps modulo 256 without raising exceptions. Modular types also support the logical operators and, or, xor and not bitwise, which makes them the right tool for bit masks, checksums, hash functions and hardware registers. The package Interfaces provides fixed-size modular types (Unsigned_8, Unsigned_16, Unsigned_32, Unsigned_64) with Shift_Left, Shift_Right and Rotate functions.

• type T is mod N; — values 0 .. N-1, wraparound arithmetic, no overflow exception. • and, or, xor, not are bitwise on modular types. • Interfaces.Unsigned_8/16/32/64 with Shift_Left/Right, Rotate_Left/Right. • Use modular types only when wraparound is intended.

Code Examples

Custom range types, predefined bounds, truncating division and the difference between rem and mod. Output: Today : 17 Low :-12 Integer range :-2147483648 .. 2147483647 Natural'First : 0 Positive'First: 1 Day'Last : 31 7 / 2 = 3, -7 / 2 =-3 -7 rem 2 =-1, -7 mod 2 = 1

Byte wraps: 250 + 10 = 4 and 0 - 1 = 255 with no exception. Byte'(0) is a qualified expression telling the compiler the literal is a Byte. Bitwise operators work directly on modular values. Output: 250 + 10 = 4 0 - 1 = 255 16#F0# and 16#0F# = 0 16#F0# or 16#0F# = 255 16#FF# xor 16#0F# = 240 not 0 = 255

Integer'Last + 1 cannot be represented, so Constraint_Error is raised and handled. Output: Integer'Last = 2147483647 Constraint_Error: integer overflow detected at run time type Index is range 0.. Size; -- Size is a variable

Size : constant := 100; type Index is range 0 .. Size;

Type bounds must be static; use a named number or constant computed at compile time. For run-time bounds use a subtype.

Next:= (Pos + 1) rem Length; -- Pos may be negative after -1 Next:= (Pos + 1) mod Length;

For wraparound indexing use mod, whose result is always non-negative for a positive divisor.

type Counter is mod 232; -- used for a quantity that must never wrap type Counter is range 0.. 231 - 1;

Modular types hide overflow bugs by wrapping; use a range type when wrapping would be an error.

What is -7 mod 3 in Ada? A. -1 B. 2 C. 1 D. -2 B. 2 — mod takes the sign of the divisor (3), and -7 = 3 × (-3) + 2.

What happens when you add 1 to a Byte (type Byte is mod 256) holding 255? A. Constraint_Error B. It becomes 0 C. It becomes 256 D. Compile error

B. It becomes 0 — modular arithmetic wraps.

What happens with Integer'Last + 1 in Ada? A. Wraps to Integer'First B. Constraint_Error C. Undefined behaviour D. Becomes Long_Integer

B. Constraint_Error — overflow is detected.

Which predefined subtype includes 0 but no negative numbers? A. Positive B. Natural C. Integer D. Cardinal

B. Natural — 0 .. Integer'Last; Positive starts at 1.

Trace the output of the modular example.

Real output: 250 + 10 = 4 0 - 1 = 255 16#F0# and 16#0F# = 0 16#F0# or 16#0F# = 255 16#FF# xor 16#0F# = 240 not 0 = 255

with Ada.Text_IO; use Ada.Text_IO;

procedure L5_Ranges is
   type Day_Of_Month is range 1 .. 31;
   type Temperature  is range -50 .. 60;
   Today : constant Day_Of_Month := 17;
   Low   : constant Temperature := -12;
begin
   Put_Line ("Today         :" & Day_Of_Month'Image (Today));
   Put_Line ("Low           :" & Temperature'Image (Low));
   Put_Line ("Integer range :" & Integer'Image (Integer'First) & " .." & Integer'Image (Integer'Last));
   Put_Line ("Natural'First :" & Natural'Image (Natural'First));
   Put_Line ("Positive'First:" & Positive'Image (Positive'First));
   Put_Line ("Day'Last      :" & Day_Of_Month'Image (Day_Of_Month'Last));
   Put_Line ("7 / 2 =" & Integer'Image (7 / 2) & ",  -7 / 2 =" & Integer'Image ((-7) / 2));
   Put_Line ("-7 rem 2 =" & Integer'Image ((-7) rem 2) & ",  -7 mod 2 =" & Integer'Image ((-7) mod 2));
end L5_Ranges;
with Ada.Text_IO; use Ada.Text_IO;

procedure L5_Modular is
   type Byte is mod 256;              --  values 0 .. 255, arithmetic wraps
   A : Byte := 250;
   B : constant Byte := 16#0F#;
begin
   A := A + 10;                       --  260 mod 256 = 4, no exception
   Put_Line ("250 + 10 =" & Byte'Image (A));
   Put_Line ("0 - 1    =" & Byte'Image (Byte'(0) - 1));
   Put_Line ("16#F0# and 16#0F# =" & Byte'Image (16#F0# and B));
   Put_Line ("16#F0# or  16#0F# =" & Byte'Image (16#F0# or B));
   Put_Line ("16#FF# xor 16#0F# =" & Byte'Image (16#FF# xor B));
   Put_Line ("not 0 =" & Byte'Image (not Byte'(0)));
end L5_Modular;
with Ada.Text_IO; use Ada.Text_IO;

procedure L5_Overflow is
   Big : Integer := Integer'Last;
begin
   Put_Line ("Integer'Last =" & Integer'Image (Big));
   Big := Big + 1;                            --  overflow is detected, not wrapped
   Put_Line ("Unreachable:" & Integer'Image (Big));
exception
   when Constraint_Error =>
      Put_Line ("Constraint_Error: integer overflow detected at run time");
end L5_Overflow;