Delphi Berlin 10.1 缺少除零异常

7

我很惊讶没有得到除零异常。该如何恢复它?

我安装了最新的Berlin 10.1,创建了一个新项目。

procedure TForm1.Button1Click(Sender: TObject);
var
  a: Double;
begin
  a := 5/0;                     // No exception
  ShowMessage(a.ToString);      // -> 'INF'
end;

1
在我的 Berlin 版本上已确认。看起来在东京也是如此:https://community.embarcadero.com/answers/divide-by-zero-different-in-rad-studio-10-2 - Jerry Dodge
6
@Jerry:这是因为该表达式是常量表达式,而不是在运行时计算的。 被除数和除数中至少有一个必须是变量才会引发异常。 - Rudy Velthuis
3
太好了!编译器知道如何进行除以0的操作 :) - Victoria
@Victoria:实际上,编译器可能也是交给FPU处理的,但它是在编译时进行的,显然在这样做时会掩盖(一些)异常。如果零除异常被掩盖,则无限大是一个有效的结果。 - Rudy Velthuis
在西雅图我也能看到这个条件。 - UnDiUdin
2个回答

8
a := 5/0;

表达式5/0在技术术语中被称为常量表达式
常量表达式是编译器可以在不执行包含它的程序的情况下计算的表达式。常量表达式包括数字、字符串、真正的常量、枚举类型的值、特殊常量True、False和nil以及仅由这些元素与运算符、类型转换和集合构造函数组成的表达式。
因此,这个表达式是由编译器评估的,并且不是在运行时评估的。所以它的评估由编译时规则确定,不能受到运行时浮点异常掩码的影响。
这些规则指出,正数除以零等于一个特殊的IEEE754值+INF。如果你更改表达式,使其至少有一个参数不是常量表达式,则将在运行时评估它,并引发一个除以零异常。

5
您可以使用Math单元中的SetExceptionMask函数来控制触发哪些浮点异常:http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Math.SetExceptionMask 要禁用所有浮点异常,请使用以下代码:
SetExceptionMask(exAllArithmeticExceptions);

为了启用所有浮点异常,请使用以下代码:

要启用所有浮点异常,请使用:

SetExceptionMask([]);

请注意,在您的示例代码中,编译器将在编译时确定该值(因为表达式是常量),因此无论传递给SetExceptionMask的值如何,它都不会触发异常。要触发异常,您需要一个稍微复杂一些的示例,例如:
program test;
uses Math;
var
  xx,yy: double;
begin
SetExceptionMask([]);
xx := 1;
yy := 0;
halt(round(xx/yy));
end.

而要启用除零异常,可以执行以下操作:SetExceptionMask(GetExceptionMask - [exZeroDivide]); - Rudy Velthuis
3
至少这个答案的前半部分与所问问题无关。 - David Heffernan
6
在讨论浮点异常未触发的情况时,提到SetExceptionMask是相关的。当然,这可能不是OP代码中出现问题的原因,但在这里发布的代码通常被简化了,真正的代码可能会使用变量。请注意不要改变原意。 - Ville Krumlinde
@VilleKrumlinde 公正的评论,这是一个海报可能合理地遇到问题,并设法制定一个症状类似但具有不同根本原因的 [mcve] 的情况。 - Disillusioned

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接