9

当我在 Objective-C 中进行模运算时得到的结果让我有点害怕。-1 % 3 结果是 -1,这不是正确的答案:根据我的理解,它应该是 2。-2 % 3 出来是 -2,这也不正确:它应该为1。

除了 % 运算符之外,我还应该使用另一种方法来获得正确的结果吗?

4

6 回答 6

7

Objective-C 是 C99 的超集,C99 定义a % b为负时a为负。另请参阅有关模运算的维基百科条目这个 StackOverflow 问题

类似的东西(a >= 0) ? (a % b) : ((a % b) + b)(未经测试,可能有不必要的括号)应该给你你想要的结果。

于 2010-03-12T05:59:49.540 回答
3

Spencer,有一种简单的方式来考虑 mods(它是在数学中定义的方式,而不是编程方式)。它实际上相当简单:

取所有整数:

...-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 时余数为零的所有数字,即这些都是模为零的数字。

现在让我们将整个组向上移动一个。

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

这些是除以 3 时余数为 1 的所有数字,即这些都是模为 1 的所有数字。

现在让我们将整个组再上移一个。

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

这些是除以 3 时余数为 2 的所有数字,即这些都是模为 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。 ..

所有这些等价数的标准数学表示是使用类的数,这意味着取最小的非负数。

所以通常,当我考虑 mods 并且我正在处理一个负数时,我只想一次又一次地连续添加模数,直到我得到第一个 0 或正数:

如果我们正在做 mod 3,那么使用 -1,只需添加 3 一次:-1 + 3 = 2。使用 -4,添加 3 两次,因为一次还不够。如果我们加一次 +3,我们得到 -4 + 3 = -1,仍然是负数。所以我们将再次添加 +3:-1 + 3 = 2。

让我们尝试一个更大的负数,比如 -23。如果您继续添加 +3,您将获得:

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

于 2013-01-04T06:34:08.647 回答
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 方式没有任何“错误”,只是不是您所期望的。:)

于 2010-03-16T10:33:01.910 回答
1

负数取模并不像您想象的那么简单。见http://mathforum.org/library/drmath/view/52343.html

于 2010-03-12T05:46:51.347 回答
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);
}

希望有帮助!一个。

于 2013-01-10T23:31:02.193 回答
0

最后是一个可以为您提供正确答案的显式函数,但首先,这里是对讨论过的其他一些想法的解释:

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

换句话说:如果您想找到:-1 % 3,那么肯定 (a >= 0) ? (a % b) : ((a % b)+ b) 会起作用,因为您在末尾添加了((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

** 当心!确保将(浮动)铸件保留在那里。否则,它会将 a/b 划分为整数,您会得到一个意想不到的否定答案。当然,这意味着您的结果也将是一个浮点数。它将是一个写成浮点数的整数,如 2.000000,因此您可能希望将整个答案转换回整数。

(int) (a - floor((float) a/b)*b)
于 2013-01-04T06:23:37.963 回答