如何找到任何整数的10的下一个倍数?

T.T*_*.T. 42 c algorithm math

动态整数将是0到150之间的任何数字.

即 - 数字返回41,需要返回50.如果数字是10则需要返回10.数字是1需要返回10.

如果我将整数修改为小数,我以为我可以使用天花板功能......?然后使用天花板功能,并回到十进制?
唯一的事情也是必须知道这个数字是1,2或3位数(即-7 vs 94 vs 136)

有没有更好的方法来实现这一目标?

谢谢,

R S*_*hko 84

n + (10 - n % 10)
Run Code Online (Sandbox Code Playgroud)

这是如何工作的.%运算符计算除法的其余部分(因此41 % 10计算结果为1,而45 % 10计算结果为5).从10个评估中减去您需要多少到达下一个倍数.

唯一的问题是,这将把40变为50.如果你不想这样,你需要添加一个检查,以确保它不是10的倍数.

if (n % 10)
    n = n + (10 - n % 10);
Run Code Online (Sandbox Code Playgroud)

  • 为什么不`(n + 9) - ((n + 9)%10)`?这也适用于10的倍数,并且仍然只需要三个操作. (14认同)
  • 啊,这比我的快。 (2认同)
  • @Harvey:在你测量之前不要做出性能声明是明智的.编译器可能比我们所有人都聪明. (2认同)
  • 页面上有一个更简单的解决方案.n =(n + 9)/ 10. (2认同)
  • 这是最糟糕的选择之一.没有理由为此使用条件分支.这使[代码比大多数其他体面的建议更差](http://goo.gl/rFEPio) (2认同)

AnT*_*AnT 40

你可以通过整数除以10 舍入,然后将结果乘以10来做到这一点.

要划分AB四舍五入,添加B - 1A,然后把它B用"普通"的整数除法

Q = (A + B - 1) / B 
Run Code Online (Sandbox Code Playgroud)

因此,对于您的具体问题,同时在一起的事情将如下所示

A = (A + 9) / 10 * 10
Run Code Online (Sandbox Code Playgroud)

这将"捕捉" A到下一个10的倍数.

分裂和对齐的需要经常出现,通常在我的程序中,我有用于将[unsigned]整数除以四舍五入的宏

#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
Run Code Online (Sandbox Code Playgroud)

并用于将整数与下一个边界对齐

#define ALIGN_UP(a, b) (UDIV_UP(a, b) * (b))
Run Code Online (Sandbox Code Playgroud)

这将使上述看起来像

A = ALIGN_UP(A, 10);
Run Code Online (Sandbox Code Playgroud)

PS我不知道你是否需要扩展到负数.如果这样做,应该注意正确地做,取决于你需要的结果.

  • +1这是迄今为止唯一的整数算术解决方案,当n = 10时实际工作,没有额外的条件测试. (3认同)
  • 嗯,这实际上是一个众所周知且广泛使用的习语.我很惊讶我是第一个提到它的人. (3认同)

bta*_*bta 24

怎么样((n + 9) / 10) * 10

收率0 => 0,1 => 10,8 => 10,29 => 30,30 => 30,31 => 40

  • 呃...... `(n + 9) / 10` 产生 `8 => 1`,而不是你在帖子中所说的 `8 => 10`。您可能忘记将结果乘以“10”。 (2认同)
  • +1:很好的答案.我最近在bitset实现中使用过它.问:如果一个字可以保存32位,那么保持'n'位(n> = 0)需要多少个字?答:(n + 31)/ 32 (2认同)

Pet*_*des 10

tl; dr:((n + 9) / 10) * 10在更多情况下编译为最好的(最快的)asm代码,并且易于阅读和理解知道C中整数除法的人.这是一个相当常见的习语.

我还没有研究什么是需要使用负面效果的最佳选项n,因为您可能希望从零开始,而不是仍然朝向+ Infinity,具体取决于应用程序.


看看不同建议所使用的C操作,最轻量级的是Mark Dickinson(评论中):

(n+9) - ((n+9)%10)
Run Code Online (Sandbox Code Playgroud)

它看起来比几个人(包括@bta)建议的直接除法/乘法更有效:((n + 9) / 10) * 10因为它只有一个加法而不是乘法.(n+9是一个常见的子表达式,只需要计算一次.)

事实证明,两者都编译为字面上完全相同的代码,使用将除法转换为常数转换为乘法和移位的编译技巧,请参阅此Q&A了解其工作原理.与div使用商,余数或两者结果的成本相同的硬件指令不同,mul/shift方法需要额外的步骤来获得余数.因此编译器看到它可以从更便宜的计算得到相同的结果,并最终将两个函数编译为相同的代码.

这在x86,ppc和ARM以及我在Godbolt编译器资源管理器上看到的所有其他架构都是如此.在这个答案的第一个版本中,我看到了sdiv针对%10ARM64的Godbolt的gcc4.8,但是它已经不再安装了(也许是因为配置错误了?)ARM64 gcc5.4没有这样做.

Godbolt现在安装了MSVC(CL),其中一些函数编译方式不同,但我没有花时间看看哪个编译更好.


请注意,在x86的gcc输出中,乘以10便宜lea eax, [rdx + rdx*4]地完成n*5,然后add eax,eax将其加倍. imul eax, edx, 10在英特尔Haswell上会有1个周期更高的延迟,但要更短(少一个uop).gcc/clang甚至不使用它-Os -mtune=haswell:/


接受的答案(n + 10 - n % 10)甚至更便宜计算:n+10可以与之并行发生n%10,因此依赖链缩短了一步.它编译为少一条指令.

但是,它给出了10的倍数的错误答案:例如10 -> 20.建议的修复程序使用a if(n%10)来决定是否执行任何操作.这编译成a cmov,所以它比@ Bta的代码更长,更糟.如果您打算使用条件,请执行以获得负面输入的合理结果.


以下是所有建议答案的行为方式,包括负输入:

./a.out | awk -v fmt='\t%4s' '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'
       i     -22     -21     -20     -19     -18     -12     -11     -10      -9      -8      -2      -1       0       1       2       8       9      10      11      12         18      19      20      21      22
    mark     -10     -10     -10     -10       0       0       0       0       0       0       0       0       0      10      10      10      10      10      20      20         20      20      20      30      30
    igna     -10     -10     -10       0       0       0       0       0      10      10      10      10      10      10      10      10      10      20      20      20         20      20      30      30      30
    utaal    -20     -20     -20     -10     -10     -10     -10     -10       0       0       0       0       0      10      10      10      10      10      20      20         20      20      20      30      30
     bta     -10     -10     -10     -10       0       0       0       0       0      10      10      10      10      10      10      10      10      10      20      20         20      20      20      30      30
    klatchko -10     -10     -10     -10       0       0       0       0       0       0       0       0       0      10      10      10      10      10      20      20         20      20      20      30      30
    branch   -10     -10     -20       0       0       0       0     -10      10      10      10      10       0      10      10      10      10      10      20      20         20      20      20      30      30
Run Code Online (Sandbox Code Playgroud)

(转置awk程序)

Ignacio n + (((9 - (n % 10)) + 1) % 10)对于负整数"正确"工作,向+ Infinity四舍五入,但计算成本要高得多.它需要两个模运算,因此它的成本基本上是原来的两倍.它编译的x86指令大约是其两倍,其工作量大约是其他表达式的两倍.

结果打印程序(与上面的godbolt链接相同)

#include <stdio.h>
#include <stdlib.h>

int f_mark(int n) { return (n+9) - ((n+9)%10); }                   // good
int f_bta(int n) { return ((n + 9) / 10) * 10; }                   // compiles to literally identical code

int f_klatchko(int n) { return n + 10 - n % 10; }                  // wrong, needs a branch to avoid changing multiples of 10
int f_ignacio(int n) { return n + (((9 - (n % 10)) + 1) % 10); }   // slow, but works for negative
int roundup10_utaal(int n) {  return ((n - 1) / 10 + 1) * 10; }

int f_branch(int n) { if (n % 10) n += (10 - n % 10); return n; }  // gcc uses cmov after f_accepted code

int main(int argc, char**argv)
{
    puts("i\tmark\tigna\tutaal\tbta\tklatch\tbranch");
    for (int i=-25 ; i<25 ; i++)
    if (abs(i%10) <= 2 || 10 - abs(i%10) <= 2)  // only sample near interesting points
        printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", i, f_mark(i), f_accepted(i),
           f_ignacio(i), roundup10_utaal(i), f_bta(i), f_branch(i));
}
Run Code Online (Sandbox Code Playgroud)


Har*_*vey 7

如何使用整数数学:

N=41
N+=9   // Add 9 first to ensure rounding.
N/=10  // Drops the ones place
N*=10  // Puts the ones place back with a zero
Run Code Online (Sandbox Code Playgroud)