В целочисленной арифметике C# всегда ли A/b/C равен a / (b*c)?
пусть a, b и c-не большие положительные целые числа. Всегда ли A/b/c равен a / (b * c) с целочисленной арифметикой C#? Для меня, в C# это выглядит так:
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
Так что мой вопрос: делает x1 == x2 для всех a, b и c?
6 ответов:
пусть
\обозначает целочисленное деление (C#/оператор между двумяintS), и пусть/обозначает обычное математическое деление. Тогда, еслиx,y,zare положительные целые числа и проигнорировав переполнения,(x \ y) \ z = floor(floor(x / y) / z) [1] = floor((x / y) / z) [2] = floor(x / (y * z)) = x \ (y * z)здесь
a \ b = floor(a / b)прыжок с линии
[1]to line[2]выше объясняется следующим образом. Предположим, у вас есть два целых числаaиbи дробное числоfв диапазоне[0, 1). Это легко увидеть, чтоfloor(a / b) = floor((a + f) / b) [3]если в строке
[1]определитьa = floor(x / y),f = (x / y) - floor(x / y)иb = z, потом[3]означает, что[1]и[2]равны.вы можете обобщить это доказательство на отрицательные целые числа (все еще проигнорировав переполнения), но я оставлю это читателю, чтобы сохранить точку зрения простой.
по вопросу переполнения - см. ответ Эрика Липперта для хорошего объяснение! Он также принимает гораздо более строгий подход в его сообщение в блоге и ответьте, что вы должны посмотреть, если вы чувствуете, что я слишком волнистый.
мне так понравился этот вопрос, что я сделал его предметом мой блог от 4 июня 2013 года. Спасибо за отличный вопрос!
большие случаи легко найти. Например:
a = 1073741823; b = 134217727; c = 134217727;, потому что
b * cперетекает в отрицательное число.я бы добавил к этому тот факт, что в проверил арифметические, разница между
a / (b * c)и(a / b) / cможет быть разница между программой, которая работает и программа, которая выходит из строя. Если произведениеbиcпереполняет границы целого числа, то первый будет сбой в проверенном контексте.для малых положительных целых чисел, скажем, достаточно малых, чтобы вписаться в короткий, идентичность должна быть сохранена.
Тимоти Шилдс только что опубликовал доказательство; я представляю здесь альтернативное доказательство. Предположим, что все числа здесь являются неотрицательными целыми числами и ни одна из операций переполнения.
целое число деление
x / yнаходит значениеqтакое, чтоq * y + r == x, где0 <= r < y.отдел
a / (b * c)находит значениеq1такое, чтоq1 * b * c + r1 == aздесь
0 <= r1 < b * cотдела
( a / b ) / cсначала находит значениеqtтакое, чтоqt * b + r3 == aи затем находит значение
q2такое, чтоq2 * c + r2 == qtтак замените это на
qtи мы получаем:q2 * b * c + b * r2 + r3 == aздесь
0 <= r2 < cи0 <= r3 < b.две вещи, равные одному и тому же, равны друг другу, поэтому мы имеем
q1 * b * c + r1 == q2 * b * c + b * r2 + r3предположим
q1 == q2 + xдля некоторого целого числаx. Замените это и решите дляx:q2 * b * c + x * b * c + r1 = q2 * b * c + b * r2 + r3 x = (b * r2 + r3 - r1) / (b * c)здесь
0 <= r1 < b * c 0 <= r2 < c 0 <= r3 < bможете
xбыть больше нуля? Нет. У нас есть неравенства:b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * cзначит числитель этой дроби всегда меньше
b * c, так чтоxне может быть больше, чем нуль.можете
xбыть меньше нуля? Нет, по аналогичной аргументации, оставленной читателю.поэтому целое число
xравно нулю, а значитq1 == q2.
если абсолютные значения
bиcниже оsqrt(2^31)(прим. 46 300), так чтоb * cникогда не будет переполнения, значения всегда будут совпадать. Еслиb * cпереполнения, то ошибка может быть брошена вcheckedконтекст, или вы можете получить неверные значения вuncheckedконтексте.
избегая ошибок переполнения, замеченных другими, они всегда совпадают.
предположим, что
a/b=q1, Что означаетa=b*q1+r1, где0<=r1<b.
Теперь предположим, чтоa/b/c=q2, что означаетq1=c*q2+r2, где0<=r2<c.
Это значит, чтоa=b(c*q2+r2)+r1=b*c*q2+br2+r1.
Для того, чтобыa/(b*c)=a/b/c=q2, мы должны иметь0<=b*r2+r1<b*c.
Ноb*r2+r1<b*r2+b=b*(r2+1)<=b*c, как требовалось, и две операции совпадают.это не работает, если
bилиcотрицательные, но я не знайте, как целочисленное деление работает и в этом случае.
я предложу свое собственное доказательство для удовольствия. Это также игнорирует переполнение и, к сожалению, обрабатывает только положительные стороны, но я думаю, что доказательство чисто и ясно.
цель состоит в том, чтобы показать, что
floor(floor(x/y)/z) = floor(x/y/z)здесь
/является нормальным делением (во всем этом доказательстве).мы представляем частное и остаток
a/bоднозначно какa = kb + r(под этим мы подразумеваем, чтоk,rуникальны, а также Примечание|r| < |b|). Тогда мы есть:(1) floor(x/y) = k => x = ky + r (2) floor(floor(x/y)/r) = k1 => floor(x/y) = k1*z + r1 (3) floor(x/y/z) = k2 => x/y = k2*z + r2так что наша цель-просто показать, что
k1 == k2. Ну у нас есть:k1*z + r1 = floor(x/y) = k = (x-r)/y (from lines 1 and 2) => x/y - r/y = k1*z + r1 => x/y = k1*z + r1 + r/yи так:
(4) x/y = k1*z + r1 + r/y (from above) x/y = k2*z + r2 (from line 3)теперь заметьте из (2), что
r1- целое число (дляk1*zявляется целым числом по определению) иr1 < z(также по определению). Кроме того, из (1) мы знаем, чтоr < y => r/y < 1. Теперь рассмотрим суммуr1 + r/yиз (4). Претензия заключается в том, чтоr1 + r/y < zи это ясно из предыдущих утверждений (потому что0 <= r1 < zиr1является целым числом так у нас есть0 <= r1 <= z-1. Поэтому0 <= r1 + r/y < z). Таким образомr1 + r/y = r2по определениюr2(иначе было бы два остатки отx/yчто противоречит определению остатка). Следовательно, мы имеем:x/y = k1*z + r2 x/y = k2*z + r2и у нас есть наш желаемый вывод, что
k1 = k2.приведенное выше доказательство должно работать с негативами, за исключением нескольких шагов, которые вам нужно будет проверить в дополнительном случае(ах)... но я не стал проверять.