Почему целочисленное деление округляется во многих языках сценариев? - PullRequest
0 голосов
/ 20 мая 2018

На языках, которые я тестировал, - (x div y ) не равно -x div y;Я протестировал // в Python, / в Ruby, div в Perl 6; C имеет похожее поведение .

Такое поведение обычно соответствует спецификации, поскольку div обычно определяется как округление вниз результатаделение , однако оно не имеет большого смысла с арифметической точки зрения, поскольку оно заставляет div вести себя по-разному в зависимости от знака и вызывает путаницу, такую ​​как в этом постекак это делается в Python .

Есть ли какое-то конкретное обоснование для этого проектного решения, или просто div определяется таким образом с нуля?Очевидно, Гвидо ван Россум использует в своем блоге аргумент когерентности , который объясняет, как это делается в Python, но вы можете иметь когерентность, даже если захотите округлить.

(Вдохновлен этот вопрос от PMurias в IRC-канале # perl6 )

Ответы [ 5 ]

0 голосов
/ 20 мая 2018

В Википедии есть отличная статья на эту тему , включая историю и теорию.


Пока язык удовлетворяет свойству евклидова деления, что (a/b) * b + (a%b) == a, оба этажаделение и усечение деление являются последовательными и арифметически разумными.


Конечно, людям нравится утверждать, что одно очевидно правильно, а другое явно неправильно, но у него больше характер священной войны, чем разумногообсуждение, и обычно оно больше связано с выбором их раннего предпочтительного языка, чем с чем-либо еще.Они также часто склонны спорить в первую очередь о выбранном %, хотя, вероятно, имеет смысл сначала выбрать /, а затем просто выбрать %, который соответствует.

  • Настил (например,Python):
    • Не менее авторитетный, чем предполагает это Дональд Кнут.
    • %, следуя знаку делителя, очевидно, что примерно 70% всех учащихся предполагают
    • Оператор обычно читается как mod или modulo, а не remainder.
    • "C делает это" - что даже не соответствует действительности. 1
  • Усечение (например, C ++):
    • Делает целочисленное деление более совместимым с IEEE-делением с плавающей запятой (в режиме округления по умолчанию).
    • Больше процессоров реализует это.(Может быть неверным в разное время в истории.)
    • Оператор читается modulo, а не remainder (даже если на самом деле это аргумент против их точки).
    • Свойство деления концептуально больше относится к остатку, чем к модулю.
    • Оператор читается как mod, а не modulo, поэтому он должен следовать различию Фортрана.(Это может звучать глупо, но, возможно, было решающим аргументом для C99. См. эту тему .)
  • "Евклидов" (например, Паскаль - / этажи илиусекается в зависимости от знаков, поэтому % никогда не бывает отрицательным):
    • Никлаус Вирт утверждал, что никто никогда не удивляется положительным mod.
    • Реймонд Т. Боут позже утверждал, что вы можете 'Евклидово деление наивно реализуется с помощью любого из других правил.

Несколько языков предоставляют оба варианта.Обычно - как в Ada, Modula-2, некоторых Lisps, Haskell и Julia - они используют имена, связанные с mod для оператора в стиле Python и rem для оператора в стиле C ++.Но не всегда - например, Фортран называет одни и те же вещи modulo и mod (как упоминалось выше для C99).


Мы не знаем, почему Python, Tcl, Perl идругие влиятельные скриптовые языки в основном выбирали напольные покрытия.Как отмечено в вопросе, ответ Гвидо ван Россума только объясняет, почему он должен был выбрать один из трех последовательных ответов, а не почему он выбрал тот, который сделал.

Однако я подозреваю, что влияние C было ключевым.Большинство языков сценариев (по крайней мере на начальном этапе) реализованы в C и заимствуют свой инвентарь операторов из C. Определенная в C89 реализация % явно не работает и не подходит для «дружественного» языка, такого как Tcl или Python.И С называет оператор "мод".Таким образом, они идут с модулем, а не с остатком.


1.Несмотря на то, что говорится в вопросе - и многие люди используют его в качестве аргумента - C на самом деле не ведет себя так же, как Python и его друзья.С99 требует усеченного деления, а не настила.C89 допускает и то, и другое, а также любую версию мода, так что нет гарантии на свойство подразделения, и нет способа написать переносимый код, делающий целочисленное деление со знаком.Это просто сломано.

0 голосов
/ 20 мая 2018

Операции с плавающей запятой определяются IEEE754 с учетом числовых приложений и, по умолчанию, округляются до ближайшего представимого значения очень строго определенным образом.

Целочисленные операции в компьютерах не определяется общими международными стандартами.Операции, предоставляемые языками (особенно таковыми из семейства C), имеют тенденцию следовать тому, что обеспечивает базовый компьютер.Некоторые языки определяют некоторые операции более надежно, чем другие, но чтобы избежать чрезмерно сложных или медленных реализаций на доступных (и популярных) компьютерах своего времени, выберут определение, которое следует за его поведением довольно близко.

По этой причинецелочисленные операции имеют тенденцию к переносу около при переполнении (для сложения, умножения и сдвига влево) и округлению до отрицательной бесконечности при получении неточного результата (для деления и сдвига-право). Оба они являются простыми усечениями на соответствующем конце целого числа в двоичной арифметике с двумя дополнительными числами;самый простой способ обработки углового случая.

В других ответах обсуждается связь с оператором остатка или модуля, который язык может обеспечить наряду с делением.К сожалению, они имеют это задом наперед. Остаток зависит от определения деления, а не наоборот , в то время как модуль можно определить независимо от деления - если оба аргумента оказываются положительными, а округление округляется, они оказываются одинаковыми, поэтомулюди редко замечают.

Большинство современных языков предоставляют либо оператор остатка, либо оператор модуля, редко оба.Библиотечная функция может предоставлять другую операцию людям, которые заботятся о разнице, а именно: остаток сохраняет знак дивиденда, а модуль сохраняет знак делителя.

0 голосов
/ 20 мая 2018

Как сказала Паула, это из-за остатка.

Алгоритм основан на евклидовом делении .

В Ruby вы можете написать это, восстанавливая дивидендыс последовательностью:

puts (10/3)*3 + 10%3
#=> 10

В реальной жизни работает одинаково.10 яблок и 3 человека.Хорошо, вы можете разрезать одно яблоко на три, но выходя за пределы набора целых чисел.

При отрицательных числах согласованность также сохраняется:

puts (-10/3)*3 + -10%3 #=> -10
puts (10/(-3))*(-3) + 10%(-3) #=> 10
puts (-10/(-3))*(-3) + -10%(-3) #=> -10

Коэффициент всегда округляется вниз (вниз поотрицательная ось) и напоминание следующее:

puts (-10/3) #=> -4
puts -10%3 #=> 2

puts (10/(-3)) #=> -4
puts 10%(-3) # => -2

puts (-10/(-3)) #=> 3
puts -10%(-3) #=> -1 
0 голосов
/ 20 мая 2018

В идеале мы хотели бы иметь две операции div и mod, удовлетворяющие для каждой b>0:

  1. (a div b) * b + (a mod b) = a
  2. 0 <= (a mod b) < b
  3. (-a) div b = -(a div b)

Это, однако, математическая невозможность.Если бы все вышеизложенное было верным, мы бы получили

1 div 2 = 0
1 mod 2 = 1

, поскольку это единственное целочисленное решение (1) и (2).Следовательно, мы также имеем бы к (3)

0 = -0 = -(1 div 2) = (-1) div 2

, что согласно (1) подразумевает

-1 = ((-1) div 2) * 2 + ((-1) mod 2) = 0 * 2 + ((-1) mod 2) = (-1) mod 2

, что делает (-1) mod 2 < 0, что противоречит (2).

Следовательно, нам нужно отказаться от некоторого свойства среди (1), (2) и (3).

Некоторые языки программирования отказываются от (3) и делают div округлением вниз (Python, Ruby).

В некоторых (редких) случаях язык предлагает несколько операторов деления.Например, в Haskell у нас есть div,mod, удовлетворяющий только (1) и (2), аналогично Python, и у нас также есть quot,rem, удовлетворяющий только (1) и (3).Последняя пара операторов округляет деление до нуля , по цене возврата отрицательных остатков, например, у нас (-1) `quot` 2 = 0 и (-1) `rem` 2 = (-1).

C # также сдается (2),и позволяет % возвращать отрицательный остаток.Связно целочисленное деление округляет до нуля.Java, Scala, Pascal и C, начиная с C99, также принимают эту стратегию.

0 голосов
/ 20 мая 2018

Поскольку подразумевается целочисленное деление, то в полном ответе содержится остаток.

...