我有一个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
答案 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
:无符号乘法没有溢出但是请参阅有关可用于检测其他操作溢出的逻辑公式的文章。 (以上三个比较复杂,因此本来就受到支持。对于其他情况,可以直接由用户进行检查。)