如何取整整数除法的结果?


335

我在考虑使用C#或Java之类的语言时如何显示分页控件。

如果我有x个项目要以每页y个块的形式显示,那么需要多少个页面?


1
我想念什么吗?y / x + 1效果很好(前提是您知道/运算符总是四舍五入)。
rikkit 2012年

51
@rikkit-如果y和x相等,则y / x +1太高。
伊恩·纳尔逊

1
对于刚刚发现此问题的任何人,此重复问题的答案除了提供了明确的解释之外,还避免了不必要的转换为双精度避免了溢出问题。
ZX9

2
如果将IanNelson x整除y,则@IanNelson y/x + 1会太高。
Ohad Schneider

1
@ ZX9不,它不能避免溢出问题。这与Ian Nelson在此处发布的解决方案完全相同。
user247702

Answers:


478

找到了一个优雅的解决方案:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

资料来源:数字转换,罗兰·巴克豪斯(Roland Backhouse),2001年


12
-1是由于Brandon
DuRette

30
Obvious先生说:请记住确保recordsPerPage不为零
Adam Gent

7
干得好,我不敢相信C#没有整数上限。
gosukiwi 2012年

2
是的,在尝试多种复杂方法之后,我在2017年中期遇到了这个好答案。
Mifo '17

1
对于具有适当的Euclidian除法运算符的语言(例如Python),一种甚至更简单的方法是pageCount = -((-records) // recordsPerPage)
超级猫

194

在CPU级别上,转换为浮点数和返回浮点数似乎是浪费大量时间。

伊恩·尼尔森(Ian Nelson)的解决方案:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

可以简化为:

int pageCount = (records - 1) / recordsPerPage + 1;

AFAICS,它没有Brandon DuRette指出的溢出错误,并且由于它只使用一次,因此,如果它来自昂贵的函数来从配置文件中获取值,则不需要专门存储recordsPerPage。的东西。

也就是说,如果config.fetch_value使用数据库查找或其他方法,则这可能效率不高:

int pageCount = (records + config.fetch_value('records per page') - 1) / config.fetch_value('records per page');

这将创建一个您实际上不需要的变量,该变量可能具有(较小的)内存含义,并且键入过多:

int recordsPerPage = config.fetch_value('records per page')
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

这全都是一行,并且只提取一次数据:

int pageCount = (records - 1) / config.fetch_value('records per page') + 1;

5
+1,零记录仍然返回1 pageCount的问题实际上很方便,因为我仍然想要1页,显示占位符/伪造行“没有符合条件的记录”,有助于避免出现任何“ 0页数”问题您使用的分页控件。
Timothy Walters

27
请注意,对于零个记录,这两个解决方案不会返回相同的pageCount。此简化版本将为零记录返回1 pageCount,而Roland Backhouse版本将返回0 pageCount。如果这是您想要的,就很好了,但是当用C#/ Java stylee整数除法执行时,两个方程不相等。
伊恩·尼尔森

10
从尼尔森解决方案简化为简化版时,人们进行细微的编辑以使人们扫描它并丢失胸部(就像我第一次这样做!),带括号的简化为... int pageCount =((records-1)/ recordsPerPage) + 1;
戴夫·海伍德2014年

1
您应该在简化版本中添加括号,以使其不依赖于特定的操作顺序。也就是说,(((records-1)/ recordsPerPage)+ 1
Martin

1
@Ian,此答案始终不会返回1。如果您的recordsPerPage为“ 1”且有0条记录,则返回0 -1 / 1 + 1 = 0。尽管这不是一种普遍现象,但是如果允许用户调整页面大小,请务必牢记。因此,要么不允许用户的页面大小为1,要么对页面大小进行检查,或者同时检查两者(最好是避免出现意外行为)。
迈克尔(Michael)

81

对于C#,解决方案是将值转换为双精度值(因为Math.Ceiling需要双精度值):

int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);

在Java中,您应该对Math.ceil()执行相同的操作。


4
为什么当op明确要求C#时,这个答案如此之低!
felickz

2
您还需要将输出强制转换为,int因为根据输入类型Math.Ceiling返回a doubledecimal
DanM7 '10 -10-17

15
因为它效率极低
Zar Shardan

6
它可能效率不高,但非常容易理解。给定计算页数通常每个请求执行一次,因此任何性能损失都无法衡量。
贾里德·凯尔斯

1
它几乎比“(除数+(除数-1))/除数”更具可读性;还慢,需要数学库。

68

这应该给您您想要的。您肯定希望每页x个项目除以y个项目,问题是出现不均匀的数字时,因此,如果存在部分页面,我们也希望添加一页。

int x = number_of_items;
int y = items_per_page;

// with out library
int pages = x/y + (x % y > 0 ? 1 : 0)

// with library
int pages = (int)Math.Ceiling((double)x / (double)y);

5
x / y + !!(x%y)避免了类C语言的分支。几率不错,但是您的编译器还是会这样做。
Rhys Ulerich 2010年

2
+1不会像上面的答案那样溢出...尽管仅将整数转换为Math.ceiling的double,然后再次返回,这对性能敏感的代码却不是一个好主意。
齿轮

3
@RhysUlerich在c#中不起作用(无法将int直接转换为bool)。我认为rjmunro的解决方案是避免分支的唯一方法。
smead '16

18

Ian提供的整数数学解决方案很好,但存在整数溢出错误。假设变量为all int,则解决方案可以重写为使用long数学运算并避免错误:

int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;

如果recordslong,则错误仍然存​​在。模数解决方案没有错误。


4
我认为您不会在提出的方案中实际遇到此错误。2 ^ 31条记录需要分页。
rjmunro


@finnw:AFAICS,该页面上没有真实的示例,只是有关在理论上有人发现该错误的报告。
rjmunro 2011年

5
是的,我很想指出该错误。可以永久存在许多错误,而不会引起任何问题。在有人报告它之前,JDK的binarySearch实现中存在相同形式的错误已有九年之久(googleresearch.blogspot.com/2006/06/…)。我想问题是,不管您遇到这种错误的可能性有多大,为什么不先解决它?
布兰登·杜莱特

4
另外,应该注意的是,重要的不仅仅是页面的元素数量,还有页面大小。因此,如果您要构建一个库,并且有人选择不通过传递2 ^ 31-1(Integer.MAX_VALUE)作为页面大小来进行分页,则会触发该错误。
布兰登·杜莱特

7

Nick Berardi的回答的变体避免了分支:

int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));

注意:(-r >> (Integer.SIZE - 1))由的符号位组成r,重复32次(由于>>运算符的符号扩展。)如果r为零或负,则结果为0,如果r为正,则结果为-1 。因此q,如果将其减去则具有加1的效果records % recordsPerPage > 0


4

对于记录== 0,rjmunro的解决方案给出1。正确的解决方案是0。也就是说,如果您知道记录> 0(并且我确定我们都假定recordsPerPage> 0),那么rjmunro的解决方案就会给出正确的结果,并且没有任何溢出问题。

int pageCount = 0;
if (records > 0)
{
    pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required

所有整数数学解都将比任何浮点解都更有效率。


此方法不太可能成为性能瓶颈。如果是这样,您还应该考虑分支机构的成本。
finnw 2011年

4

需要扩展方法:

    public static int DivideUp(this int dividend, int divisor)
    {
        return (dividend + (divisor - 1)) / divisor;
    }

这里没有检查(溢出DivideByZero等),如果您愿意,可以随时添加。顺便说一下,对于那些担心方法调用开销的人来说,像这样的简单函数可能还是会被编译器内联,所以我不认为这是要关注的地方。干杯。

PS,您可能会发现也意识到这一点很有用(它会得到剩余的):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);

1
这是不正确的。例如:DivideUp(4, -2)返回0(应为-2)。它仅适用于从答案或函数接口不清楚的非负整数
Thash

6
推销,为什么不做一些有用的操作,例如添加一些额外的检查,然后在数字为负数的情况下,而不是投票否决我的答案,并错误地做出笼统的声明:“这是不正确的”,而实际上这只是一条优势案件。我已经明确表示您应该首先进行其他检查:“这里没有检查(溢出,DivideByZero等),如果愿意,可以随意添加。”
尼古拉斯·彼得森

1
该问题提到“我特别在考虑如何显示分页控件 ”,因此无论如何负数都将超出范围。同样,做一些有用的事情,如果需要的话,建议增加检查,这是团队合作精神。
尼古拉斯·彼得森

我不是无礼的意思,对不起,如果您这么认为的话。问题是“如何对整数除法的结果取整”。作者提到了分页,但是其他人可能有不同的需求。我认为,如果您的函数以某种方式反映出它不适用于负整数会更好,因为从接口中看不出来(例如,不同的名称或参数类型)。要使用负整数,例如,您可以取除数和除数的绝对值,然后将结果乘以其符号。
Thash


1

我执行以下操作,处理所有溢出:

var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;

如果结果为0,请使用此扩展名:

public static bool IsDivisble(this int x, int n)
{
           return (x%n) == 0;
}

此外,对于当前页码(不要求,但可能有用):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;

0

在零测试中删除分支的替代方法:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);

不知道这是否可以在C#中使用,应该在C / C ++中使用。


-1

您可以迭代其结果的通用方法可能引起您的兴趣:

public static Object[][] chunk(Object[] src, int chunkSize) {

    int overflow = src.length%chunkSize;
    int numChunks = (src.length/chunkSize) + (overflow>0?1:0);
    Object[][] dest = new Object[numChunks][];      
    for (int i=0; i<numChunks; i++) {
        dest[i] = new Object[ (i<numChunks-1 || overflow==0) ? chunkSize : overflow ];
        System.arraycopy(src, i*chunkSize, dest[i], 0, dest[i].length); 
    }
    return dest;
}

番石榴有类似的方法(Lists.partition(List, int)),具有讽刺意味的size()是,生成的方法List(截至r09)遭受了Brandon DuRette的答案中提到的溢出错误。
finnw 2011年

-2

我有类似的需要,我需要将分钟转换为小时和分钟。我使用的是:

int hrs = 0; int mins = 0;

float tm = totalmins;

if ( tm > 60 ) ( hrs = (int) (tm / 60);

mins = (int) (tm - (hrs * 60));

System.out.println("Total time in Hours & Minutes = " + hrs + ":" + mins);

-2

与以下解决方案相比,以下内容应做得更好,但以性能为代价(由于浮点计算为0.5 * rctDenominator):

uint64_t integerDivide( const uint64_t& rctNumerator, const uint64_t& rctDenominator )
{
  // Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)
  return (rctDenominator == 0) ? 0 : (rctNumerator + (int)(0.5*rctDenominator)) / rctDenominator;
}

-4

您需要进行浮点除法,然后使用上限函数将值四舍五入到下一个整数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.