如何将整数四舍五入到最接近的整数倍数?
How do I round an integer down to the nearest multiple of a number?
我正在尝试将整数四舍五入为最接近的数字倍数。
假设数字等于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
) 的位置。
好吧,如果您只是尝试使用整数除数(您除以的数字),那么总有 ((int) x / theNumber) * theNumber)
,但它看起来是否更好取决于您。但是,我不知道有什么更好的方法,但是您可以重新利用 this thread.
上的某些答案。
I'm trying to round an integer to the nearest multiple of a number.
[...]
Is there a more efficient way to do this than x -= (x % the_number)
?
一般情况下,不会。有类似效率的替代方案,例如
x = (x / the_number) * the_number
,但你不会用少于两次的算术运算来完成它。 (此外 -
在某些架构上比 *
更有效,并且 /
和 %
通常在效率上相当)。
但是,如果您想截断为 2 的预先已知的幂,则可以通过使用单个按位 &
屏蔽掉低位来实现。例如,要截断到最接近的 16 的小倍数 (== 0x10),您可以编写
x &= ~0xf; // truncate an int x to a multiple of 16
只有一种检查方法:
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 可能会更慢。
如果您怀疑该值可能是 2 的幂,那么检查一位除数并使用移位和掩码处理 mod 是值得的。对于少量的位数,return 递减,但可能是值得的。
if (!(b & (b-1))) {
x = a & (b-1);
} else {
x = a % b;
}
我有点惊讶 cpu 没有简化这个简单的案例,它似乎在随机 64 位 mods 上产生了 4-5 的因子。
我正在尝试将整数四舍五入为最接近的数字倍数。
假设数字等于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
) 的位置。
好吧,如果您只是尝试使用整数除数(您除以的数字),那么总有 ((int) x / theNumber) * theNumber)
,但它看起来是否更好取决于您。但是,我不知道有什么更好的方法,但是您可以重新利用 this thread.
I'm trying to round an integer to the nearest multiple of a number. [...] Is there a more efficient way to do this than
x -= (x % the_number)
?
一般情况下,不会。有类似效率的替代方案,例如
x = (x / the_number) * the_number
,但你不会用少于两次的算术运算来完成它。 (此外 -
在某些架构上比 *
更有效,并且 /
和 %
通常在效率上相当)。
但是,如果您想截断为 2 的预先已知的幂,则可以通过使用单个按位 &
屏蔽掉低位来实现。例如,要截断到最接近的 16 的小倍数 (== 0x10),您可以编写
x &= ~0xf; // truncate an int x to a multiple of 16
只有一种检查方法:
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 可能会更慢。
如果您怀疑该值可能是 2 的幂,那么检查一位除数并使用移位和掩码处理 mod 是值得的。对于少量的位数,return 递减,但可能是值得的。
if (!(b & (b-1))) {
x = a & (b-1);
} else {
x = a % b;
}
我有点惊讶 cpu 没有简化这个简单的案例,它似乎在随机 64 位 mods 上产生了 4-5 的因子。