Давайте посмотрим, что делает 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
, что отображается на -126
в 8-битном представлении со знаком.Правая сторона, однако, уменьшается до 65
;таким образом, давая вам законный контрпример.
Чтобы избежать этого, вы можете использовать старый добрый тип Int
;или попросите Python не использовать подписанное деление с помощью UDiv
, см. здесь: 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
что больше соответствует тому, что вы ожидали, я полагаю.