第9课|整型类型详解

190 阅读10分钟

本课目标

完成本课后,你将能够:

  • 深入理解预定义整数类型与机器整数类型的区别

  • 掌握用户定义整数的范围约束与存储优化

  • 熟练应用模类型的回绕特性与位运算场景

  • 处理整数运算的溢出、饱和与异常策略

  • 选择适合嵌入式与高性能场景的整数方案


一、预定义整数类型

1.1 标准整数类型族

Ada预定义了完整的整数类型层次:

-- 根整数类型(理论无限精度,实际受实现限制)
type Universal_Integer is range -(2**63) .. 2**63-1;  -- 概念性

-- 标准预定义整数
type Integer   is range -(2**31) .. 2**31-1;   -- 通常32位
type Natural   is range 0 .. Integer'Last;      -- 非负整数
type Positive  is range 1 .. Integer'Last;      -- 正整数

-- 长整数与短整数(实现定义)
type Long_Integer    is range -(2**63) .. 2**63-1;  -- 通常64位
type Short_Integer   is range -(2**15) .. 2**15-1;  -- 通常16位
type Short_Short_Integer is range -(2**7) .. 2**7-1; -- 通常8位

关键洞察Integer 的具体范围由编译器实现决定,但保证至少16位。GNAT中通常为32位。


1.2 整数类型属性全解

procedure Integer_Attributes is
   X : Integer := 100;
begin
   
   -- 范围属性
   Put_Line ("Integer'First = " & Integer'Image (Integer'First));
   Put_Line ("Integer'Last  = " & Integer'Image (Integer'Last));
   Put_Line ("Integer'Range = " & Integer'Image (Integer'First) & 
             " .. " & Integer'Image (Integer'Last));
   
   -- 大小属性
   Put_Line ("Integer'Size  = " & Integer'Image (Integer'Size) & " bits");
   Put_Line ("Integer'Object_Size = " & 
             Integer'Image (Integer'Object_Size));
   
   -- 运算属性(基属性)
   Put_Line ("X'Valid = " & Boolean'Image (X'Valid));  -- 检查值是否有效
   Put_Line ("100'Image = " & 100'Image);              -- " 100"
   Put_Line ("Integer'Value (""42"") = " & 
             Integer'Image (Integer'Value ("42")));
   
   -- 机器表示
   Put_Line ("Integer'Machine_Radix = " & 
             Integer'Image (Integer'Machine_Radix));  -- 通常为2
   
end Integer_Attributes;

整数属性速查表

属性返回值说明
'First最小值类型下界
'Last最大值类型上界
'Range范围First .. Last
'Size位数最小存储位数
'Object_Size位数实际分配位数(可能大于Size)
'Value整数从字符串解析
'Image字符串转为字符串(前导空格)
'Width整数图像字符串最大长度
'Valid布尔值是否在范围内
'Machine_Radix整数基数(通常为2)
'Machine_Emin整数最小指数(浮点用)

二、用户定义整数类型

2.1 范围约束整数

-- 基本语法:type 名称 is range 下界 .. 上界;

-- 嵌入式常见定义
type Byte   is range -128 .. 127;           -- 8位有符号
type UByte  is range 0 .. 255;              -- 8位无符号(用Natural更好)
type Word   is range 0 .. 65_535;           -- 16位无符号
type SWord  is range -32_768 .. 32_767;     -- 16位有符号
type DWord  is range 0 .. 4_294_967_295;    -- 32位无符号
type SDWord is range -2_147_483_648 .. 2_147_483_647; -- 32位有符号

-- 特定应用定义
type Percentage is range 0 .. 100;          -- 百分比
type Angle_Degrees is range 0 .. 359;       -- 角度(0-359)
type Year is range 1900 .. 2100;            -- 年份范围
type Month is range 1 .. 12;                -- 月份
type Day_Of_Month is range 1 .. 31;         -- 日(需配合月份验证)

存储优化:编译器自动选择最小足够存储:

类型定义GNAT通常选择说明
range -128 .. 127Signed_88 位
range 0 .. 255Unsigned_88 位
range -32_768 .. 32_767Signed_1616 位
range 0 .. 65_535Unsigned_1616 位
range -(2**31) .. 2**31-1Signed_3232 位

2.2 大小子句强制存储

当需要精确控制存储大小时,使用 Size 子句:

package Precise_Types is

   type Byte is range -128 .. 127;
   for Byte'Size use 8;           -- 强制8位存储
   
   type Status_Flags is range 0 .. 255;
   for Status_Flags'Size use 8;
   pragma Pack (Status_Flags);    -- 紧凑排列(通常与Size配合)
   
   -- 记录中的位域控制
   type Control_Register is record
      Enable    : Boolean;
      Interrupt : Boolean;
      Mode      : range 0 .. 3;   -- 2位
      Reserved  : range 0 .. 15;  -- 4位
   end record;
   
   for Control_Register use record
      Enable    at 0 range 0 .. 0;   -- 位0
      Interrupt at 0 range 1 .. 1;   -- 位1
      Mode      at 0 range 2 .. 3;   -- 位2-3
      Reserved  at 0 range 4 .. 7;   -- 位4-7
   end record;
   
   for Control_Register'Size use 8;  -- 总共8位 = 1字节

end Precise_Types;

三、模类型(Modular Types)

3.1 模类型的核心特性

模类型是无符号整数,运算自动回绕(wrap-around),无溢出异常:

-- 定义:type 名称 is mod 模数;
-- 值域:0 到 模数-1

type Byte_Mod is mod 2**8;   -- 0 .. 255,256回绕到0
type Word_Mod is mod 2**16;  -- 0 .. 65535
type Bit_Field is mod 2**32; -- 32位位域

-- 使用
B : Byte_Mod := 255;
B := B + 1;  -- 结果为0,无异常!
B := B - 1;  -- 回到255

-- 位运算(模类型的优势)
Flags : Byte_Mod := 2#1010_1010#;
Mask  : constant Byte_Mod := 2#0000_1111#;

Result := Flags and Mask;   -- 按位与:2#0000_1010#
Result := Flags or Mask;    -- 按位或
Result := Flags xor Mask;   -- 按位异或
Result := not Flags;        -- 按位取反(在范围内)
Result := Flags shl 2;      -- 左移(GNAT特定)
Result := Flags shr 1;      -- 右移(GNAT特定)

3.2 模类型 vs 有符号整数

特性模类型(mod)有符号整数(range)
值域0 .. 模数-1负值 .. 正值
溢出行为自动回绕抛出 Constraint_Error
位运算原生支持需转换
适用场景位掩码、哈希、循环计数通用算术、物理量
与负数运算禁止(编译错误)允许

3.3 模类型实战:位掩码

package GPIO_Driver is

   type Pin_Number is range 0 .. 31;
   
   -- 32位寄存器,每位控制一个引脚
   type Pin_Mask is mod 2**32;
   
   -- 常量定义
   PIN_0  : constant Pin_Mask := 2**0;
   PIN_1  : constant Pin_Mask := 2**1;
   PIN_LED: constant Pin_Mask := 2**13;  -- 板载LED
   PIN_ALL: constant Pin_Mask := Pin_Mask'Last;
   
   -- 操作子程序
   function Make_Mask (Pin : Pin_Number) return Pin_Mask
      is (2**Natural (Pin));
   
   procedure Set_Pins   (Mask : Pin_Mask);  -- 置位
   procedure Clear_Pins (Mask : Pin_Mask);  -- 清零
   procedure Toggle_Pins(Mask : Pin_Mask);  -- 翻转
   
   -- 使用示例
   -- Set_Pins (PIN_LED or PIN_0);  -- 同时设置LED和引脚0

end GPIO_Driver;

package body GPIO_Driver is

   -- 假设硬件寄存器地址
   GPIO_SET   : Pin_Mask
      with Address => System'To_Address (16#4000_1000#), Volatile;
   GPIO_CLR   : Pin_Mask
      with Address => System'To_Address (16#4000_1004#), Volatile;
   GPIO_TOGGLE: Pin_Mask
      with Address => System'To_Address (16#4000_1008#), Volatile;

   procedure Set_Pins (Mask : Pin_Mask) is
   begin
      GPIO_SET := Mask;  -- 写1置位
   end Set_Pins;

   procedure Clear_Pins (Mask : Pin_Mask) is
   begin
      GPIO_CLR := Mask;  -- 写1清零
   end Clear_Pins;

   procedure Toggle_Pins (Mask : Pin_Mask) is
   begin
      GPIO_TOGGLE := Mask;  -- 写1翻转
   end Toggle_Pins;

end GPIO_Driver;

四、整数运算与溢出处理

4.1 标准运算与溢出检查

procedure Integer_Operations is
   type Small is range -100 .. 100;
   X, Y, Z : Small;
begin
   X := 50;
   Y := 60;
   
   -- 加法(可能溢出)
   Z := X + Y;  -- 110 > 100,抛出Constraint_Error!
   
   -- 减法(可能下溢)
   Z := X - Y;  -- -50 - 60 = -110 < -100,异常!
   
   -- 乘法(高风险)
   Z := X * 3;  -- 150 > 100,异常!
   
   -- 除法(除零检查)
   Z := X / 0;  -- 除零异常!
   
   -- 幂运算
   Z := 2 ** 7;  -- 128 > 100,异常!
end Integer_Operations;

4.2 溢出处理策略

策略1:使用更大范围类型

type Safe_Small is range -10_000 .. 10_000;
X, Y : Safe_Small := 100;
Z : Safe_Small := X * Y;  -- OK,不会溢出

策略2:显式检查(防御式编程)

procedure Safe_Add (A, B : in Small; Result : out Small; OK : out Boolean) is
   Temp : Integer;  -- 使用更大范围临时存储
begin
   Temp := Integer (A) + Integer (B);
   
   if Temp in Small'Range then
      Result := Small (Temp);
      OK := True;
   else
      OK := False;  -- 调用者处理溢出
      -- 或:Result := Small'Last;  -- 饱和处理
   end if;
end Safe_Add;

策略3:模类型自动回绕

type Wrapping_Byte is mod 256;
X, Y : Wrapping_Byte := 200;
Z : Wrapping_Byte := X + Y;  -- 400 mod 256 = 144,无异常

策略4:饱和运算(自定义)

function Saturating_Add (A, B : Small) return Small is
   Temp : Integer := Integer (A) + Integer (B);
begin
   if Temp > Small'Last then
      return Small'Last;   -- 正饱和
   elsif Temp < Small'First then
      return Small'First;  -- 负饱和
   else
      return Small (Temp);
   end if;
end Saturating_Add;

4.3 禁用溢出检查(危险!)

pragma Suppress (Overflow_Check);  -- 全局禁用(不推荐)

-- 或局部禁用
procedure Fast_But_Unsafe is
   pragma Suppress (Overflow_Check);
begin
   -- 此区域内无溢出检查,回绕行为未定义!
   null;
end Fast_But_Unsafe;

警告:仅在绝对必要时使用,且必须证明不会溢出。


五、机器整数与表示

5.1 预定义机器整数

Interfaces 包提供精确位宽的机器整数:

with Interfaces;

procedure Machine_Integers is
   use Interfaces;
   
   -- 有符号
   X8  : Integer_8   := -128;    -- 8位
   X16 : Integer_16  := -32768;  -- 16位
   X32 : Integer_32  := -2**31;  -- 32位
   X64 : Integer_64  := -2**63;  -- 64位
   
   -- 无符号
   U8  : Unsigned_8  := 255;     -- 8位
   U16 : Unsigned_16 := 65535;   -- 16位
   U32 : Unsigned_32 := 2**32-1; -- 32位
   U64 : Unsigned_64 := 2**64-1; -- 64位
   
begin
   -- 与C代码交互时直接使用这些类型
   null;
end Machine_Integers;

5.2 表示子句高级应用

package Hardware_Types is

   type Device_Status is record
      Powered_On   : Boolean;
      Error_Flag   : Boolean;
      Busy         : Boolean;
      Data_Ready   : Boolean;
      Error_Code   : range 0 .. 15;
   end record;
   
   -- 强制位布局,匹配硬件寄存器
   for Device_Status use record
      Powered_On at 0 range 0 .. 0;
      Error_Flag at 0 range 1 .. 1;
      Busy       at 0 range 2 .. 2;
      Data_Ready at 0 range 3 .. 3;
      Error_Code at 0 range 4 .. 7;
   end record;
   
   for Device_Status'Size use 8;
   pragma Pack (Device_Status);
   
   -- 验证大小
   pragma Compile_Time_Error 
      (Device_Status'Size /= 8, "Status must be 8 bits");

end Hardware_Types;

六、完整示例:安全计数器

------------------------------------------------------------------------------
--                         Safe_Counter                                     --
--                                                                          --
--              演示整数类型、溢出处理与模类型的综合应用                    --
------------------------------------------------------------------------------

generic
   Initial_Value : Integer := 0;
   Min_Value     : Integer := Integer'First;
   Max_Value     : Integer := Integer'Last;

package Safe_Counter is

   type Counter is private;
   
   -- 创建计数器
   function Create (Start : Integer := Initial_Value) return Counter
      with Pre => Start in Min_Value .. Max_Value;
   
   -- 递增(饱和或异常)
   procedure Increment (C : in out Counter; By : in Positive := 1);
   
   -- 递减(饱和或异常)
   procedure Decrement (C : in out Counter; By : in Positive := 1);
   
   -- 当前值
   function Value (C : Counter) return Integer;
   
   -- 检查是否饱和
   function Is_Saturated (C : Counter) return Boolean;
   
   -- 溢出异常
   Overflow_Error  : exception;
   Underflow_Error : exception;

private

   type Counter is record
      Val        : Integer range Min_Value .. Max_Value;
      Saturated  : Boolean := False;
   end record;

end Safe_Counter;

package body Safe_Counter is

   function Create (Start : Integer := Initial_Value) return Counter is
   begin
      return (Val => Start, Saturated => False);
   end Create;

   procedure Increment (C : in out Counter; By : in Positive := 1) is
      Temp : Integer;
   begin
      if C.Saturated then
         return;  -- 保持饱和状态
      end if;
      
      Temp := Integer (C.Val) + By;
      
      if Temp > Max_Value then
         C.Val := Max_Value;
         C.Saturated := True;
         raise Overflow_Error with "Counter saturated at maximum";
      else
         C.Val := Temp;
      end if;
   end Increment;

   procedure Decrement (C : in out Counter; By : in Positive := 1) is
      Temp : Integer;
   begin
      if C.Saturated then
         return;
      end if;
      
      Temp := Integer (C.Val) - By;
      
      if Temp < Min_Value then
         C.Val := Min_Value;
         C.Saturated := True;
         raise Underflow_Error with "Counter saturated at minimum";
      else
         C.Val := Temp;
      end if;
   end Decrement;

   function Value (C : Counter) return Integer is
   begin
      return C.Val;
   end Value;

   function Is_Saturated (C : Counter) return Boolean is
   begin
      return C.Saturated;
   end Is_Saturated;

end Safe_Counter;

七、本课总结

  • 预定义整数: Integer / Natural / Positive 等,范围由实现决定

  • 用户定义整数: type T is range A .. B ,编译器自动优化存储

  • 模类型: type T is mod N ,无符号、自动回绕、原生位运算

  • 溢出策略:大类型、显式检查、模回绕、饱和运算、禁用检查(危险)

  • 机器整数: Interfaces 包提供 Integer_8/16/32/64 等精确位宽类型


八、课后练习

1.类型设计:为"秒表"设计类型,支持0-5999秒(99分59秒),溢出归零。

2.位域操作:实现一个8位状态寄存器,包含3个标志位和5位错误码字段。

3.饱和运算:为 Percentage (0-100)类型实现完整的饱和算术运算包。

4.哈希函数:使用模类型实现简单的32位FNV-1a哈希算法。

5.溢出分析:分析以下代码的潜在溢出问题并提出修复方案:

type Index is range 1 .. 10_000;
type Array_Type is array (Index) of Integer;

procedure Sum (A : Array_Type; Total : out Integer) is
begin
   Total := 0;
   for I in A'Range loop
      Total := Total + A (I);
   end loop;
end Sum;

九、下节预告

第10课|浮点与定点类型

我们将:

  • 深入IEEE 754浮点表示与精度问题

  • 掌握用户定义浮点的精度与范围控制

  • 理解定点类型的delta与范围约束

  • 学习数值稳定性分析与误差控制策略


关键术语表

模类型:无符号整数类型,运算自动按模回绕

大小子句for T'Size use N ,强制类型的存储位数

表示子句:控制类型的内存布局,用于硬件寄存器映射

饱和运算:溢出时钳位到最大/最小值,而非异常或回绕

机器整数Interfaces 包中定义的固定位宽整数类型