【问题标题】:Weird Objective-C Mod Behavior for Negative Numbers负数的奇怪 Objective-C Mod 行为
【发布时间】:2010-11-02 15:51:57
【问题描述】:

所以我认为负数,当修改后应该放入正空间......我不能在objective-c中发生这种情况

我希望这样:

-1 % 3 = 2
 0 % 3 = 0
 1 % 3 = 1
 2 % 3 = 2

但是得到这个

-1 % 3 = -1
 0 % 3 = 0
 1 % 3 = 1
 2 % 3 = 2

为什么会这样?有解决方法吗?

【问题讨论】:

  • Clojure 是我遇到的第一个正确实现 mod 的语言。耶!
  • @ToddOwen:Python 也能做到这一点
  • @Todd,Lisp 两者都有。
  • 使用 frem(a,b) — 您期望的模数(标准数学中使用的那种)在编码中称为“余数”。 C 有 fmod() 和 frem(),你使用 mod (aka "%"),你需要使用 rem。数学中的取模 === 代码中的余数 (rem)。愚蠢,我知道。
  • 我注意到 frem(a,b) 仅在 GNU C 中,而不是在 Obj-C 中。相当于是这样的:a-b*floor((float)a/(float)b)

标签: objective-c modulo


【解决方案1】:
result = n % 3;
if( result < 0 ) result += 3;

不要按照其他答案中的建议执行额外的 mod 操作。它们非常昂贵且不必要。

【讨论】:

  • 条件比模数好还是差,难道不取决于处理器吗?
  • 我从未见过除法或模数不是最慢操作的处理器。检查负数减少到测试位的值,因此通常是最快的指令之一。
  • 是的。这可能是对的。我认为在 Core Duo Intels 中,一个部门大约是 15-17 个周期。我看到现在很多人都试图避免分支。但是有了一个模组,这个分支可能是值得的。
  • 据我了解,避免分支背后的想法是防止管道停滞。但是执行两个 mod 可能无论如何都会使管道停滞不前;第二个 mod 操作需要等待第一个的结果。
  • if() 几乎肯定会比第二个 mod 快;任何体面的优化编译器都可以将 if() 转换为 cmov(条件移动)指令,该指令将执行相同的操作而无需分支。
【解决方案2】:

在 C 和 Objective-C 中,除法和取模运算符执行向零截断。如果a / b &gt; 0a / bfloor(a / b),否则如果a / b &lt; 0ceiling(a / b)a == (a / b) * b + (a % b) 总是如此,除非 b 为 0。因此,positive % positive == positivepositive % negative == positivenegative % positive == negativenegative % negative == negative(您可以计算出所有 4 种情况的逻辑,虽然有点棘手)。

【讨论】:

    【解决方案3】:

    如果 n 的范围有限,则只需将大于最小值的绝对值的已知常数倍数 3 相加即可得到所需的结果。

    例如,如果 n 限制为 -1000..2000,那么您可以使用以下表达式:

    result = (n+1002) % 3;
    

    确保最大值加上你的常数在求和时不会溢出。

    【讨论】:

      【解决方案4】:

      我们有语言问题:

      math-er-says: 我把这个数字加上那个数字 mod other-number code-er-hears:我将两个数字相加,然后将结果除以其他数字 code-er-says:负数呢? 数学家说:什么?字段 mod other-number 没有负数的概念? code-er-says:字段是什么? ...
      • 此对话中的数学人员正在谈论在循环数轴上进行数学运算。如果你从底部减去,你会绕到顶部。
      • 代码人员正在谈论计算余数的运算符。

      在这种情况下,您需要数学家的 mod 运算符并使用余数函数。您可以将余数运算符转换为数学家的 mod 运算符,方法是检查每次减法时是否跌落底部。

      【讨论】:

      • 自然数定义除法,使得 (n+d)/d==(n/d)+1。真实的数字也是如此。实数也支持 -(n/d)==(-n)/d,但自然数不支持。整数可能支持一个公理或另一个公理,但不能同时支持两者。虽然编程语言可以支持整数的第一个公理并且有些人确实如此,但 FORTRAN 的创建者决定支持第二个公理,许多其他语言也纷纷效仿。
      【解决方案5】:

      我也期望一个正数,但我从 ISO/IEC 14882:2003 中找到了这个:编程语言 -- C++,5.6.4(在 Wikipedia article on the modulus operation 中找到):

      二元 % 运算符产生第一个表达式除以第二个表达式的余数。 .... 如果两个操作数都是非负数,则余数是非负数;如果不是,余数的符号是​​实现定义的

      【讨论】:

      • 对于模糊的规格万岁。 :|
      【解决方案6】:

      如果这将是行为,并且您知道它会是,那么对于m % n = r,只需使用r = n + r。如果您不确定这里会发生什么,请使用r = r % n

      编辑:总结一下,使用r = ( n + ( m % n ) ) % n

      【讨论】:

      • 如果你刚才说他应该做 (-1 + 3) % 3 = 2,我同意。 :-)
      • 我会说做 ( 3 + ( -1 % 3 ) ) % 3 == (3 + (-1) ) % 3 == ( 2 % 3 ) == 2
      • 明白了。那挺好的。也可以使用条件(if 或三元)来完成。
      【解决方案7】:

      JavaScript 也这样做。我已经被它抓住了几次。将其视为围绕零的反映,而不是延续。

      【讨论】:

        【解决方案8】:

        为什么:因为这是在 C 标准中指定 mod 运算符的方式(请记住,Objective-C 是 C 的扩展)。它让我认识的大多数人(比如我)感到困惑,因为它令人惊讶,你必须记住它。

        至于解决方法:我会使用 uncleo's。

        【讨论】:

        • 是的,就像你在 y 轴上减去上去一样令人困惑。这只是语言设计者早期的偏好之一。一开始很烦人;以后可能有用。事实上,我敢打赌,当程序员必须设计紧凑的函数来降低程序中的字节数时,以这种方式工作的 mod 具有很大的实用性,同样的原因是数组从索引 0 而不是索引 1 开始,它使数学更加紧凑跨度>
        【解决方案9】:

        UncleO 的答案可能更可靠,但如果你想在一行上做,并且你确定负值不会比 mod 的单次迭代更负 (例如,如果您在任何时候最多只减去 mod 值)您可以将其简化为一个表达式:

        int result = (n + 3) % 3;
        

        由于您无论如何都在做 mod,将 3 添加到初始值没有任何效果 除非 n 是负数(但不小于 -3)在这种情况下,它会导致结果是预期的正模数。

        【讨论】:

          【解决方案10】:

          余数有两种选择,符号取决于语言。 ANSI C 选择被除数的符号。我怀疑这就是你看到 Objective-C 也这样做的原因。 See the wikipedia entry as well.

          【讨论】:

            【解决方案11】:

            不仅是 java 脚本,几乎所有语言都显示错误的答案' coneybeare 说的是正确的,当我们有模式时,我们必须得到余数 余数只不过是除法后剩下的,它应该是一个正整数......

            如果你检查数字行你可以理解

            我在 VB 中也面临同样的问题,这让我不得不强行添加额外的检查,例如 如果结果是否定的,我们必须将除数添加到结果中

            【讨论】:

              【解决方案12】:

              而不是a%b

              使用:a-b*floor((float)a/(float)b)

              您期望 余数 并且正在使用 。在数学中它们是相同的,在 C 中它们是不同的。 GNU-C 有 Rem() 和 Mod(),objective-c 只有 mod() 所以你必须使用上面的代码来模拟 rem 函数(这和数学世界中的 mod 一样,但在编程中不是世界[至少对于大多数语言])


              还请注意,您可以为此定义一个易于使用的宏。

              #define rem(a,b) ((int)(a-b*floor((float)a/(float)b)))

              然后你可以在你的代码中使用rem(-1,3),它应该可以正常工作。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-08-05
                • 2020-02-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-04-29
                相关资源
                最近更新 更多