Оператор по модулю в Objective-C возвращает неправильный результат - PullRequest
9 голосов
/ 12 марта 2010

Я немного испуган результатами, которые я получаю, когда делаю арифметику по модулю в Objective-C. -1% 3 выходит за -1, что не является правильным ответом: по моему пониманию, оно должно быть 2. -2% 3 выходит за -2, что тоже не правильно: быть 1.

Есть ли другой метод, который я должен использовать, кроме оператора%, чтобы получить правильный результат?

Ответы [ 6 ]

7 голосов
/ 12 марта 2010

Objective-C является надмножеством C99, а C99 определяет a % b как отрицательное, когда a отрицательно. См. Также запись в Википедии об операции Modulo и этого вопроса StackOverflow .

Что-то вроде (a >= 0) ? (a % b) : ((a % b) + b) (которое не было проверено и возможно содержит лишние скобки) должно дать вам желаемый результат.

3 голосов
/ 04 января 2013

Спенсер, есть простой способ думать о модах (как это определено в математике, а не в программировании). Это на самом деле довольно просто:

Взять все целые числа:

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

Теперь давайте подумаем о коэффициентах 3 (если вы рассматриваете мод 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 ...

Это все числа, которые имеют остаток от 1 при делении на 3, т. Е. Все те, которые имеют значение 1.

Теперь давайте снова сместим всю эту группу на единицу.

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

Это все числа, которые имеют остаток 2 при делении на 3, т. Е. Это все те, которые имеют значение 2.

Вы заметите, что в каждом из этих случаев выбранные числа разнесены на 3. Мы всегда берем каждое третье число, потому что мы рассматриваем по модулю 3. (Если бы мы делали мод 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, добавьте 3 дважды, потому что одного недостаточно. Если мы добавим +3 один раз, мы получим -4 + 3 = -1, что все еще отрицательно. Итак, мы снова добавим +3: -1 + 3 = 2.

Давайте попробуем большее отрицательное число, например -23. Если вы продолжите добавлять +3, вы получите:

-23, -20, -17, -14, -11, -8, -5, -2, 1. Мы получили положительное число, поэтому мы останавливаемся. Остаток равен 1, и именно эту форму обычно используют математики.

2 голосов
/ 16 марта 2010

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 голос
/ 11 января 2013

У меня была такая же проблема, но я решил это! Все, что вам нужно сделать, это проверить, является ли число положительным или отрицательным, и если оно отрицательное, вам нужно добавить еще одно число:

//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.

1 голос
/ 12 марта 2010

Модуль с отрицательными числами не так прост, как вы думаете.Смотри http://mathforum.org/library/drmath/view/52343.html

0 голосов
/ 04 января 2013

Явная функция, которая даст вам правильный ответ, находится в конце, но сначала приведем объяснение некоторых других обсуждаемых идей:

На самом деле, (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)
...