当编码与散列、随机数生成甚至密码术有关的高效算法时,常见的构造是表达式“-n%n”。我的经验是它让许多程序员感到困惑,所以让我们进一步研究它。
此表达式中的百分号(%)表示模运算。它返回整数除法的余数。为了简化讨论,让我们假设范围0严格为正,因为除以零会产生问题。
我们应该注意前面的减号(-)。求值的是一元运算符,而不是减号。“-RANGE%RANGE&34;”和“0-RANGE%RANGE&34;”之间存在差异。它们一点也不等同。它们实际上会给您不同的值;后一个表达式始终为零。这是因为运营的优先权。求反运算优先于模运算,模运算优先于减法运算。因此,您可以将“-range%range";”重写为“(-range)%range";。您可以将“0-Range%Range";”写为“0-(Range%Range)”。
当变量范围是带符号整数时,表达式-range%range为零。在只包含有符号整数的编程语言(如Java)中,此表达式始终为零。
因此,让我们假设变量范围是无符号类型,正如它应该是的那样。在这种情况下,表达式通常为非零。
当变量范围是无符号类型时,Visual Studio可能会对表达式范围不满意。最近的Visual Studio返回以下警告:
尽管如此,我相信它在C++、Go和许多其他编程语言中都是定义良好的操作。Jonathan Adamczewski有一篇关于这个主题的整篇博客文章,其中建议用Microsoft Visual Studio团队对C++标准的历史偏差来最好地解释Visual Studio警告。(请注意,当前的Visual Studio团队似乎致力于未来的标准。)。
我最喜欢的定义是-range由range+(-range)=0定义。也就是说,它是这样的值,当您将其添加到Range时,您会得到零。数学家会说它是“加性逆”。在编程语言(如GO和C++)中,无符号整数不能溢出,那么每个整数值总是有且只有一个加法倒数。
您可以定义这个没有一元否定的加法逆:如果max是您可以表示的最大值,那么您可以将-range替换为max-range+1。或者,可能更简单地,定义为(0-range)。事实上,在SWIFT编程语言中,这一特定行表示如下:
SWIFT语言有两个减法运算,一个不允许溢出(通常的‘-’),另一个允许溢出(‘&;-’)。SWIFT强迫我们编写这么多代码有点不方便,但我们必须承认,结果可能不太可能让优秀的程序员感到困惑。
总结:您只需检查一行代码就可以学到很多东西。换句话说,编程是一项比乍看起来更深入、更复杂的实践。正如我昨天告诉我的一个学生的那样:你不应该一直阅读并理解新的代码。