英文:
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
这等价于寻找整数x
和k
,使得:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论