证明溢出检查表达式正确

时间:2019-02-09 21:45:11

标签: z3 smt

我有一个C ++函数,其中包含一个可以用手简单证明的表达式(溢出检查)。我正在考虑进行优化,这对我来说似乎是正确的,我无法找到它的反例,但我想确保它是正确的。我听说过Z3,似乎很合适。我写了一个公式,Z3说Typeface,但是问题部分是我不相信自己所获得的结果,因为我不完全理解自己做对的事情是否正确(基于之前获得的非凡的结果,对此我感到恐惧,但这是我的错,我意识到了。)

C ++函数:

unsat

我想证明(除法是整数,没有实数部分):

template <typename T> bool add(int radix, int digit, T& n) { assert(radix > 2); assert(radix <= 36); assert(digit >= 0); assert(digit < radix); T max = std::numeric_limits<T>::max(); assert(max >= radix); // the overflows check if ((n > (max / radix)) || ((n * radix) > (max - digit))) return false; n = n * radix + digit; return true; } <=> (n > (max / radix)) || ((n * radix) > (max - digit))

或更普遍的是,如果这些表达式在n > ((max - digit) / radix)(n * radix) > max时始终为真

我有Z3代码:

(n * radix + digit) > max

https://rise4fun.com/Z3/po1h

1 个答案:

答案 0 :(得分:2)

对我很好。从风格上讲,我会这样写:

(define-fun oldCheck () Bool
  (or (> n (div max radix)) 
      (> (* n radix) (- max digit))))

(define-fun newCheck () Bool
      (> n (div (- max digit) radix)))

(assert (distinct oldCheck newCheck)) 

可以清楚地说明您要检查的内容。您可以放心,优化过程很顺利!

关于不同的注释

distinct谓词在SMTLib文档的第37页上定义:http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf

如果您恰好传递了两个参数,它实质上等效于取反的等价物。但是,对于> 2个参数,其行为是不同的:如果传递更多的参数,则会确保成对不等式。 (也就是说,它们必须彼此不同。)在许多问题中都非常方便。

也可以将> 2个参数传递给=,并确保所有参数都相等。但是请注意,当您有两个以上的参数时,取反的相等性和唯一性将变得不同:例如2 2 3并非全部相等,但它们也不都是distinct。我希望这一点很清楚。

关于上溢/下溢检查的注意事项

Patrick提出了溢出检查和使用机器整数的问题,他正确地担心这些情况是正确的。我认为Nikita已经通过确保明确的界限来处理特定的用例。但是,不能太小心!为此,z3实际上内置了溢出检查原语。有关详细信息,请参见Nikolaj的精彩论文:https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/z3prefix.pdf

z3提供的基元是:

  • bvsmul_noovfl:有符号乘法没有溢出
  • bvsmul_noudfl:有符号乘法没有下溢
  • bvumul_noovfl:无符号乘法没有溢出

但是请参阅有关可用于检测其他操作溢出的逻辑公式的文章。 (以上三个比较复杂,因此本来就受到支持。对于其他情况,可以直接由用户进行检查。)