Objective-C中的模运算符返回错误的结果

9

在使用Objective-C进行模运算时,我对结果感到有些惊讶。-1 % 3的结果是-1,这不是正确的答案:根据我的理解,它应该是2。-2 % 3的结果是-2,这也是不正确的:它应该是1。

除了%运算符,是否还有其他方法可以得到正确的结果?


你好,想知道关于模运算的这些答案是否有符合你的需求? - kris
1
使用frem(a,b) - 在编程中,你所期望的取模运算(即标准数学中使用的)在编码中称为“余数”。C语言中有fmod()和frem()函数,而你现在使用的是mod(又称“%”符号),因此你需要使用rem。数学中的模运算等同于编码中的余数(rem)。这听起来很蠢,我知道。 - Albert Renshaw
有人提醒我,frem(a,b) 只存在于 GNU C 中,而没有被引入 Obj-C。相当的代码是:a-b*floor((float)a/(float)b) - Albert Renshaw
5个回答

7

太好了!它对我有用,但是我遇到了两种情况导致错误:(1) -n mod n会给出n而不是0。(2) 当将其定义为宏时,一些括号混淆了。最终我做了这个:#define module(a, b) (a >= 0 || -(a) == (b)) ? (a)%(b) : ((a)%(b) + b) - kahlo

3
Spencer,有一种简单的方式来理解模数(在数学上定义,而不是编程)。它实际上非常简单:
取所有整数:
...-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 ...
现在让我们考虑3的倍数(如果您考虑mod 3)。让我们从0和正的3的倍数开始:
...-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 ...
这些都是被3整除余数为零的数字,即这些都是mod为零的数字。
现在让我们将这整个组向上移动一个。
...-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 ...
这些都是被3整除余数为1的数字,即这些都是mod为1的数字。
现在让我们再次将整个组向上移动一个。
...-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 ...
这些都是被3整除余数为2的数字,即这些都是mod为2的数字。
您会注意到,在每种情况下,所选数字的间隔为3。我们总是取每第三个数字,因为我们考虑模3。(如果我们正在进行mod 5,则会取每个第五个数字)。
因此,您可以将此模式向后推进负数。只需保持3的间距即可。您将获得这三个同余类(在数学中称为特殊类型的等价类):

...-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ...

...-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ...

...-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ...

所有等价数字的标准数学表示法是使用该类的“余数”,这意味着取最小的非负数。

因此,通常情况下,当我考虑模数并处理负数时,我只想着不断添加模数,直到获得第一个0或正数:

如果我们使用模3,则对于-1,只需添加3次:-1 + 3 = 2。 对于-4,添加2次3,因为一次不够。如果我们添加+3一次,我们得到-4 + 3 = -1,仍然是负数。所以我们将再次添加+3:-1 + 3 = 2。

让我们尝试一个更大的负数,如-23。如果您不断添加+3,则会得到:

-23,-20,-17,-14,-11,-8,-5,-2,1。我们得到了一个正数,所以我们停止。余数为1,这是数学家通常使用的形式。


2

ANSI C99 6.5.5 乘法运算符-

6.5.5.5:除法运算符/的结果是第一个操作数除以第二个操作数的商;求模运算符%的结果是余数。在这两种运算中,如果第二个操作数的值为零,则行为未定义。

6.5.5.6:当整数相除时,除法运算符/的结果是代数商,任何小数部分都将被舍去(*90)。如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a

*90:这通常被称为“朝零截尾”。

您所考虑的模数行为类型称为“模运算”或“数论”风格的模数/余数。使用模运算/数论定义的模运算符,得到负结果是无意义的。这显然不是C99定义和使用的模数行为风格。C99方式没有什么“问题”,只是不符合您的预期。 :)


1
我曾经遇到过同样的问题,但是我解决了!你只需要检查这个数字是正数还是负数,如果是负数,就需要再加上一个数字:
//neg 
// -6 % 7 = 1
int testCount = (4 - 10);
if (testCount < 0) {
  int  moduloInt = (testCount % 7) + 7; // add 7
    NSLog(@"\ntest modulo: %d",moduloInt);
}
else{
  int moduloInt = testCount % 7;
    NSLog(@"\ntest modulo: %d",moduloInt);
}

// pos
// 1 % 7 = 1
int testCount = (6 - 5);
if (testCount < 0) {
  int  moduloInt = (testCount % 7) + 7; // add 7
    NSLog(@"\ntest modulo: %d",moduloInt);
}
else{
  int moduloInt = testCount % 7;
    NSLog(@"\ntest modulo: %d",moduloInt);
}

希望这有所帮助!A。

0

先来解释一些其他讨论过的想法,最后再给出一个明确的函数来得到正确的答案:

实际上,只有当负数 a 在 b 的一个倍数范围内时,(a >= 0) ? (a % b) : ((a % b)+ b) 才能得出正确的答案。

换句话说:如果你想要找到 -1 % 3 的结果,那么这个公式可以工作,因为你在 ((a % b) + b) 中把它加回来了。

-1 % 3 = -1,然后 -1 + 3 = 2,这就是正确答案。

然而,如果你尝试使用 a = -4 和 b = 3 进行计算,则无法得出正确答案:

-4 % 3 = -4,但是 -4 + 3 = -1

虽然从技术上讲,这也等同于2(模3),但我认为这不是你要找的答案。你可能期望的是规范形式:答案应该始终是介于0和n-1之间的非负数。

你需要两次加上+3才能得到答案:

-4 + 3 = -1
-1 + 3 = 2

这里有一种明确的方法来实现它:

a - floor((float) a/b)*b

** 注意!确保保留(float)转换。否则,它将把a/b作为整数除以,对于负数,您将得到意外的答案。当然,这意味着您的结果也将是一个浮点数。它将是一个写成浮点数的整数,例如2.000000,因此您可能希望将整个答案转换回整数。

(int) (a - floor((float) a/b)*b)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接