为什么 Z3 "rounding" 小实数为 1.0/0.0?

Why is Z3 "rounding" small reals to 1.0/0.0?

我正在使用 Z3 的 python API 进行一些线性实数运算。我遇到过非常接近于零的实数以某种方式转换为 1.0/0.0 的情况。这反过来会导致 Z3 的 C++ 部分出现浮点异常。

例如我有以下 Python 程序:

from z3 import *
s = Solver()
s.add(0.00001 * Real("a") + 0.00001 * Real("b") > 0.0)
print(s.to_smt2())
result = s.check()
print(result)
print(s.model())

产生以下输出:

; benchmark generated from python API
(set-info :status unknown)
(declare-fun b () Real)
(declare-fun a () Real)
(assert
 (let ((?x10 (+ (* (/ 1.0 0.0) a) (* (/ 1.0 0.0) b))))
 (> ?x10 0.0)))
(check-sat)

Floating point exception (core dumped)

如果我将第 3 行替换为

#s.add(Q(1,100000) * Real("a") + Q(1, 100000) * Real("b") > 0.0)

我得到了预期的输出。

有没有人有解释以及如何使用普通的 Python 浮点数让它工作的方法?

我无法复制这个。当我 运行 你的程序时,我得到:

; benchmark generated from python API
(set-info :status unknown)
(declare-fun b () Real)
(declare-fun a () Real)
(assert
 (let ((?x10 (+ (* (/ 1.0 100000.0) a) (* (/ 1.0 100000.0) b))))
 (> ?x10 0.0)))
(check-sat)

sat
[b = 50000, a = 0]

其中没有您所说的 (/ 1.0 0) 字词。

您使用的是什么版本的 z3?也许您有一个旧版本,其中有一个已修复的错误?我在 4.8.13(来自 GitHub master),不过你也可以使用最新的官方版本 4.8.12。

NB 话虽如此,我认为您使用 Q(1, 100000) 的“解决方法”实际上是更可取的,因为它会避免各种浮点精度问题。除非你正在解决浮点逻辑问题,否则你真的不应该以这种方式使用浮点数转换,因为在表示问题上会有陷阱。

事实证明,Z3 并没有按原样使用浮点数,而是将其转换为字符串,然后尝试解析该字符串。对于小数字,Python 的 str() 默认为科学记数法,Z3 显然无法正确解析它。使用 "{:.20f}".format(my_small_float) 或类似的显式转换解决了我的问题。