Большинство процессоров позволяют начинать с двух операндов, каждый размером с регистр, и умножать их вместе, чтобы получить результат, заполняющий два регистра.
Например, на x86, если вы умножаете два 32-битные числа, вы получите верхние 32 бита результата в EDX и младшие 32 бита результата в EAX.Если вы умножите два 64-битных числа, вы получите результаты в RDX и RAX.
На других процессорах используются другие регистры, но применяется та же основная идея: один регистр, умноженный на один регистр, дает результат, которыйзаполняет два регистра.
C и C ++ не предоставляют простой способ воспользоваться этой возможностью.Когда вы работаете с типами, меньшими int
, входные операнды преобразуются в int
, затем умножаются числа, и в результате получается int.Если входные данные больше, чем int, то они умножаются на один и тот же тип, а результат - того же типа.Ничего не делается для того, чтобы принять во внимание, что результат в два раза больше, чем типы входов, и практически каждый процессор на земле будет давать результат, вдвое больший, чем каждый вход индивидуально.
Есть, конечно,способы борьбы с этим.Самым простым является базовый фактор, который мы выучили в начальной школе: возьмите каждое число и разбейте его на верхнюю и нижнюю половинки.Затем мы можем умножить эти части по отдельности: (a + b) * (c + d) = ac + ad + bc + bd.Поскольку каждое из этих умножений имеет только половину ненулевых битов, мы можем выполнить каждый фрагмент арифметики как половинную операцию, получая полноразмерный результат (плюс один бит, полученный из сложения).Например, если мы хотим выполнить 64-битное умножение на 64-битном процессоре, чтобы получить 128-битный результат, мы разбили бы каждый 64-битный ввод на 32-битные части.Тогда каждое умножение даст 64-битный результат.Затем мы сложили бы куски вместе (с подходящими сдвигами битов), чтобы получить наш окончательный 128-битный результат.
Но, как отметил Питер, когда мы делаем это, компиляторы не достаточно умны, чтобы понять, что мыВы пытаетесь выполнить и превратить эту последовательность умножений и сложений обратно в единичное умножение, получая результат, вдвое больший, чем каждый вход.Вместо этого оно переводит выражение довольно непосредственно в серию умножений и сложений, поэтому это занимает где-то примерно в четыре раза больше времени, чем могло бы иметь одиночное умножение.