让我们看看Z3在做什么:
import z3
A = z3.BitVec('A', 8)
B = z3.BitVec('B', 8)
C = z3.BitVec('C', 8)
s = z3.Solver()
s.add((A*B)/C == A*(B/C))
print s.sexpr()
当你运行这个脚本时,你会得到:
$ python a.py
(declare-fun C () (_ BitVec 8))
(declare-fun B () (_ BitVec 8))
(declare-fun A () (_ BitVec 8))
(assert (= (bvsdiv (bvmul A B) C) (bvmul A (bvsdiv B C))))
啊,它使用 bvmul 和 bvsdiv 超过 8 位向量。事实证明,乘法对于有符号无符号无关紧要,但除法确实如此。因此,映射实际上是为了将结果映射到-128 到127 的范围,而不是(我怀疑你可能已经预料到)到0 到255。
因此,如果您进行数学运算,则左侧会减少到 -63,因为乘法会产生 5762,它会以有符号 8 位表示形式映射到 -126。但是,右侧减少为65;从而给你一个合法的反例。
为避免这种情况,您可以使用旧的Int 类型;或使用UDiv 告诉 Python 不要使用有符号除法,请参见此处:https://z3prover.github.io/api/html/namespacez3py.html#a64c02a843a4ac8781dd666a991797906
如果使用UDiv,可以得到更好的反例:
>>> import z3
>>> A = z3.BitVec('A', 8)
>>> B = z3.BitVec('B', 8)
>>> C = z3.BitVec('C', 8)
>>> z3.prove(z3.UDiv(A*B, C) == A*z3.UDiv(B, C))
counterexample
[A = 95, B = 140, C = 41]
>>> ((95*140)%256/41)%256
5
>>> (95*((140/41)%256))%256
29
这更符合我的预期。