Delphi中如何检测运算结果是否超出数值类型的最大值限制?
上述代码的判断逻辑完全失效的核心原因:Delphi 默认对整数运算采用静默回绕的溢出处理机制。32位有符号
Integer的最大值为2147483647($7FFFFFFF),两个最大值相加时,二进制加法结果为$FFFFFFFE,对应有符号整数的回绕值为-2。这个回绕过程在赋值完成时就已经发生,后续拿已经回绕的结果和MaxInt做大小比较,永远不可能得到成立的判断结果。
可落地的溢出识别方案
方案1:启用编译器内置溢出检测
可以在项目全局配置中打开「溢出检查」开关,也可以通过局部编译指令精准控制检测范围:在需要检测的代码段前加{$OVERFLOWCHECKS ON},代码段结束后再用{$OVERFLOWCHECKS OFF}恢复默认设置。开启后只要整数运算出现目标类型可取值范围外的结果,程序会直接抛出EIntOverflow运行时异常,不需要手动编写边界判断逻辑,直接在异常捕获分支处理溢出场景即可。这种方式的检测覆盖全场景,不存在逻辑漏洞。方案2:运算前前置边界预判
不开启异常检测的场景下,不能在运算完成后用回绕值做判断,必须在执行加法前根据数值大小预判溢出风险。对于有符号整数a、b,二者相加超出类型最大值的充要条件是:a和b均为正数,且a > 类型最大值 - b;二者相加低于类型最小值的充要条件是:a和b均为负数,且a < 类型最小值 - b。
对应代码的修正逻辑如下:procedure TForm1.FormClick(Sender: TObject); var _Integer1, _Integer2: Integer; begin _Integer1 := MaxInt; // 运算前预判是否会正溢出 if (_Integer1 > 0) and (_Integer1 > MaxInt - _Integer1) then begin Caption := '运算结果超出Integer取值范围'; end else begin _Integer2 := _Integer1 + _Integer1; Caption := IntToStr(_Integer2); end; end;这种纯数值判断的方式性能开销极低,适合对运行效率要求高的场景。
方案3:用更大范围的类型承接中间运算
选择取值范围远大于原目标类型的数值类型承接运算结果,从根源上避免运算过程中的静默回绕。比如处理32位Integer的运算时,用64位的Int64类型存储中间结果,两个MaxInt相加的结果远小于Int64的取值上限,运算过程不会发生回绕,后续直接拿这个未回绕的结果和原类型的最大值做比较即可:procedure TForm1.FormClick(Sender: TObject); var _Integer1, _Integer2: Integer; TempResult: Int64; begin _Integer1 := MaxInt; TempResult := Int64(_Integer1) + _Integer1; if TempResult > MaxInt then begin Caption := '运算结果超出Integer取值范围'; end else begin _Integer2 := Integer(TempResult); Caption := IntToStr(_Integer2); end; end;
注意:整数溢出的本质是二进制位的回绕,一旦回绕发生,原数值的大小关系、正负属性都会完全失效,所有基于运算完成后的数值编写的判断逻辑,都无法准确识别溢出场景。
内容的提问来源于stack exchange,提问作者SHIN JaeGuk

