Как округлить целое число до ближайшего кратного числа? - PullRequest
1 голос
/ 03 мая 2019

Я пытаюсь округлить целое число до ближайшего кратного числа.

Скажем, число равно 80.

Есть ли более эффективный способ сделать эточем x -= (x % the_number)?

Вот пример:

#include <stdio.h>

int main(void)
{
        int x = 191;
        int the_number = 80;
        printf("x == %d\n", x);
        x -= (x % the_number);
        printf("x - (x %% the_number) == %d\n", x);
        return 0;
}

Вот еще один пример.Это не полностью рабочая программа, как предыдущая, но она понятнее того, что я пытаюсь сделать:

#define LIGHT_GRAY 0x0700
#define COLUMNS 80

void pm_putchar(int c, void **ptr)
{
        c &= 0xff;
        c |= LIGHT_GRAY;

        if(c == '\n' | LIGHT_GRAY)
                *ptr += COLUMNS;
        else if(c == '\r' | LIGHT_GRAY)
                *ptr -= (*ptr % COLUMNS);

        **(short **)ptr = (short)c;
        (*ptr) += 2;
}

В приведенном выше примере предположим, что *ptr равно или выше местоположения VGAТекстовый буфер (0x0b8000).

Ответы [ 4 ]

4 голосов
/ 03 мая 2019

Ну, если вы пытаетесь работать только с целочисленным делителем (число, на которое делите), всегда есть ((int) x / theNumber) * theNumber), но решать, будет ли это лучше, решать вам.Тем не менее, я не знаю лучшего способа, но вы можете переопределить некоторые ответы на этой теме .

3 голосов
/ 03 мая 2019

есть только один способ проверить:

int r1(int r, const int n)
{
    return r -= (r % n);
}

int r2(int r, const int n)
{
    return (r / n) * n;
}

и сгенерированный код

r1:
        mov     eax, edi
        cdq
        idiv    esi
        mov     eax, edi
        sub     eax, edx
        ret
r2:
        mov     eax, edi
        cdq
        idiv    esi
        imul    eax, esi
        ret

метод div / mul имеет меньше инструкций, но imul, вероятно, будет медленнее, учитывая время ожидания и время выполнения.

3 голосов
/ 03 мая 2019

Я пытаюсь округлить целое число до ближайшего кратного числа.[...] Есть ли более эффективный способ сделать это, чем x -= (x % the_number)?

В общем случае нет.Существуют альтернативы с аналогичной эффективностью, такие как

x = (x / the_number) * the_number

, но вы не собираетесь делать это с менее чем двумя арифметическими операциями.(Кроме того, - более эффективен, чем * на некоторых архитектурах, а / и % обычно примерно эквивалентны по эффективности).

Если вы хотите урезать до заранее известного значениястепень 2, однако, тогда вы можете сделать это, маскируя младшие биты с помощью одного побитового &.Например, чтобы укоротить до ближайшего нижнего кратного 16 (== 0x10), вы можете написать

x &= ~0xf;  // truncate an int x to a multiple of 16
0 голосов
/ 03 мая 2019

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

if (!(b & (b-1))) {
    x = a & (b-1);
} else {
    x = a % b;
}

Я немного удивлен, что процессор не сокращает этот простой случай, он, кажется, имеет коэффициент 4-5 на случайных 64-битных модах.

...