为什么 "time==0.5" 不是 Modelica 语言中的离散表达式?
Why "time==0.5" isn't a discrete expression in Modelica language?
我建立了一个简单的模型来理解“离散表达式”的概念,这里是代码:
model Trywhen
parameter Real B[ :] = {1.0, 2.0, 3.0};
algorithm
when time>=0.5 then
Modelica.Utilities.Streams.print("message");
end when;
annotation (uses(Modelica(version="3.2.3")));
end Trywhen;
但是在检查模型时,我收到一条错误消息,显示“time==0.5”不是离散表达式。
如果我将 time==0.5
更改为 time>=0.5
,模型将通过检查。
如果我使用 if-clause
到 when-clause
,该模型工作正常,但警告显示“无法比较 Real 类型的变量是否相等。”
我的问题是:
- 为什么
time==0.5
不是离散表达式?
- 为什么不能比较实数类型的变量是否相等?在比较两个 Real 类型的变量时,这似乎很常见。
第一个问题不重要,因为time==0.5
是不允许的。
第二个问题很重要:
比较实数是否相等在其他语言中很常见,也是常见的错误来源 - 除非特别小心。
在某些混合了 80 位和 64 位浮点数(或带来性能损失)的处理器(如 Intel)上,仅仅使用处理器的浮点比较是一个非常糟糕的想法,并且在其他情况下,它也可能无法按预期工作。在这种情况下 0.5 可以表示为浮点数,但 0.1 和 0.2 不能。
通常 abs(x-y)<eps
是一个不错的选择,但这取决于预期用途,并且 eps 取决于其他因素;不仅是机器精度,还有用于计算 x 和 y 的算法及其误差传播。
在 Modelica 中,问题比在许多其他语言中更严重,因为允许工具更多地优化表达式(包括符号操作)——这使得为 eps 找出一个好的值变得更加困难。
所有这些问题意味着决定不允许比较相等 - 并且需要更合适的东西。
特别是如果你知道你只会从一个方向接近平等,你就可以避免很多问题。在这种情况下,time
是递增的,所以如果它在某个事件中是 >0.5
,则在以后的事件中不会是 <=0.5
,并且 when
只会在第一次触发表达式变为真。
因此when time>=0.5
只会触发一次,大约在time==0.5的时候触发,是一个不错的选择。但是,可能会有一些数值不准确,因此它可能会在 0.500000000000001 时触发。
我建立了一个简单的模型来理解“离散表达式”的概念,这里是代码:
model Trywhen
parameter Real B[ :] = {1.0, 2.0, 3.0};
algorithm
when time>=0.5 then
Modelica.Utilities.Streams.print("message");
end when;
annotation (uses(Modelica(version="3.2.3")));
end Trywhen;
但是在检查模型时,我收到一条错误消息,显示“time==0.5”不是离散表达式。
如果我将 time==0.5
更改为 time>=0.5
,模型将通过检查。
如果我使用 if-clause
到 when-clause
,该模型工作正常,但警告显示“无法比较 Real 类型的变量是否相等。”
我的问题是:
- 为什么
time==0.5
不是离散表达式? - 为什么不能比较实数类型的变量是否相等?在比较两个 Real 类型的变量时,这似乎很常见。
第一个问题不重要,因为time==0.5
是不允许的。
第二个问题很重要: 比较实数是否相等在其他语言中很常见,也是常见的错误来源 - 除非特别小心。
在某些混合了 80 位和 64 位浮点数(或带来性能损失)的处理器(如 Intel)上,仅仅使用处理器的浮点比较是一个非常糟糕的想法,并且在其他情况下,它也可能无法按预期工作。在这种情况下 0.5 可以表示为浮点数,但 0.1 和 0.2 不能。
通常 abs(x-y)<eps
是一个不错的选择,但这取决于预期用途,并且 eps 取决于其他因素;不仅是机器精度,还有用于计算 x 和 y 的算法及其误差传播。
在 Modelica 中,问题比在许多其他语言中更严重,因为允许工具更多地优化表达式(包括符号操作)——这使得为 eps 找出一个好的值变得更加困难。
所有这些问题意味着决定不允许比较相等 - 并且需要更合适的东西。
特别是如果你知道你只会从一个方向接近平等,你就可以避免很多问题。在这种情况下,time
是递增的,所以如果它在某个事件中是 >0.5
,则在以后的事件中不会是 <=0.5
,并且 when
只会在第一次触发表达式变为真。
因此when time>=0.5
只会触发一次,大约在time==0.5的时候触发,是一个不错的选择。但是,可能会有一些数值不准确,因此它可能会在 0.500000000000001 时触发。