为什么z3返回错误结果

huangapple go评论70阅读模式
英文:

Why z3 return wrong result

问题

我正在学习关于z3的知识。以下是您提供的代码和问题:

from z3 import *

x = BitVec("x", 32)
solver = Solver()
solver.add(x % 7 == 3)

if solver.check() == sat:
    print(solver.model())

我得到的结果是 x = 3205381361。但是 3205381361 % 7 = 0。为什么会发生这种情况,谢谢。

如有疑问,请随时提出。

英文:

Im learning about z3. Here is the code:

from z3 import *

x = BitVec("x", 32)
solver = Solver()
solver.add(x % 7 == 3)

if solver.check() == sat:
    print(solver.model())

The result i got x = 3205381361. But 3205381361 % 7 = 0. Why this happend, thanks

答案1

得分: 3

主要问题在于溢出。当你说BitVec("x", 32)时,意思是你有一个32位的字符串。没有分配给它任何数值。也就是说,你没有指定这个应该被视为有符号数还是无符号数。(请注意,这是一个设计选择,我个人认为并不是最佳选择;但这是z3的做法。)

有符号和无符号算术是位向量上的不同操作。(有时它们是一致的,但并不总是如此。)取模运算是其中之一,其中有差异。当你使用%时,z3将其解释为有符号取模运算。所以,当它打印出3205381361时,那是极其误导的:你需要将其解释为有符号的32位数。有符号的32位数的范围是-(2^-31), 2^31-1。所以,数字3205381361实际上意味着-1089585935。(确实令人困惑:你可以通过环绕到2^32来进行简单的转换,注意3205381361 - 2^32 = -1089585935)。

但现在你可以像往常一样进行算术运算:

>>> (-1089585935) % 7
3

所以它满足了你的方程。

如何“修复”这个问题呢?嗯,在某种意义上,没有什么需要修复的;只是你写的东西不是你想要的。如果你想要无界整数,可以使用Int(通过声明x = Int("x")),从而避免整个有符号-无符号的问题。(但这并不是万能的,因为有时你确实需要使用机器整数,例如在建模实际的计算机程序时。)

或者你可以说你只想要无符号数,可以使用URem

from z3 import *

x = BitVec("x", 32)
solver = Solver()
solver.add(URem(x, 7) == 3)

if solver.check() == sat:
    print(solver.model())

这会打印出:

[x = 4236335589]

这可能是你一开始想要的。

URem 函数在这里有文档:https://z3prover.github.io/api/html/namespacez3py.html#aec21d4855bd67ee88b5448d68c97a5d4

总之,如果你在使用位向量,需要根据是否需要有符号或无符号语义来小心你的操作。请参阅https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV以获取详细的操作列表。(另一个常见的陷阱是比较,这也会根据有符号性质而有所不同。请参见我提供的链接中的 bvsle vs bvule。)不幸的是,z3py 混淆了所有这些操作,除非你仔细考虑你正在执行的操作,否则可能会得到令人惊讶但仍然正确的结果。

请记住这个座右铭:在 SMTLib 位向量中,操作是有符号/无符号的,而不是值。这与大多数编程语言的做法恰恰相反,因此经常会引起混淆。

英文:

The main issue here is in overflow. When you say BitVec("x", 32) all that means is you have a string of 32 bits. There's no numerical value assigned to it. That is, you do not specify whether this should be treated as a signed number, or an unsigned number. (Note that this is a design choice, and one that is not the best in my opinion; but this is what z3 does.)

Signed and unsigned arithmetic are different operations on bit-vectors. (Sometimes they coincide, but not always.) Modulus is one of them where there's a difference. When you use %, z3 interprets that as a signed-modulus operation. So, when it prints 3205381361, that is extremely misleading: You need to interpret it as a signed 32-bit number. A signed 32-bit number has the range -(2^-31), 2^31-1. So, the number 3205381361 actually means -1089585935. (Confusing indeed: You can do a simple conversion by wrapping over 2^32, note that 3205381361 - 2^32 = -1089585935).

But now you can do the arithmetic as usual:

>>> (-1089585935) % 7
3

so it satisfies your equation.

How to "fix" this? Well, in a sense there's nothing to fix; just what you wrote didn't mean what you intended. You can use an Int if you want unbounded integers (by declaring x = Int("x")), and thus avoid the whole signed-unsigned business. (But that's not a panacea, because sometimes you really want to work with machine integers, when modeling actual computer programs for instance.)

Or you can say you only meant unsigned numbers by using URem instead:

from z3 import *

x = BitVec("x", 32)
solver = Solver()
solver.add(URem(x,7) == 3)

if solver.check() == sat:
   print(solver.model())

This prints:

[x = 4236335589]

which is probably what you were looking for int he first place.

The function URem is documented here: https://z3prover.github.io/api/html/namespacez3py.html#aec21d4855bd67ee88b5448d68c97a5d4

To sum up, if you're using bit-vectors, you need to be careful about your operations based on whether you want signed or unsigned semantics. See https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV for a detailed list of operations. (Another common gotcha is comparisons, which also differ with signedness. See, for instance bvsle vs bvule in the link I provided.) z3py, unfortunately, conflates all these and unless you're careful which operation you're doing, you can get surprising (but still correct!) results.

Here's the motto to remember: In SMTLib bitvectors, operations are signed/unsigned, not the values. Which is the exact opposite of what most programming languages do, hence the constant confusion about it.

答案2

得分: 2

这个解决方案实际上是可以的。这些不是常规整数,而是适用于32位(从0到2^32 - 1)的整数,对它们进行的操作都是模2**32。

对于这个方程,我们寻找整数x,使得:

x % 7 == 3

这等价于寻找整数xk,使得:

x = 7*k + 3

对于x=3205381361(您的解决方案),我们得到k=4139312162

(4139312162*7 + 3)%(2**32) == 3205381361

因此

3205381361 % 7 == 3
英文:

Actually, this solution is ok. Those are not regular integers, but integers that fit 32 bits (from 0 to 2^32 - 1) and operations made on them are all mod 2**32.

For this equation we look for integer x such that:

x % 7 == 3

which is equivalent to looking for integers x and k such that:

x = 7*k + 3

For x=3205381361 (your solution) we got k=4139312162:

(4139312162*7 + 3)%(2**32) == 3205381361

and hence

3205381361 % 7 == 3

huangapple
  • 本文由 发表于 2023年7月17日 17:33:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76703142.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定