정수 나누기 결과를 반올림하는 방법은 무엇입니까?


335

특히 C # 또는 Java와 같은 언어를 사용할 때 페이지 매김 컨트롤을 표시하는 방법을 생각하고 있습니다.

페이지 당 y 단위로 x 개의 항목을 표시하려는 경우 몇 개의 페이지가 필요합니까?


1
뭔가 빠졌습니까? y / x + 1은 훌륭하게 작동합니다 (/ 연산자가 항상 반올림된다는 것을 알고있는 경우).
rikkit

51
@rikkit-y와 x가 같으면 y / x + 1이 너무 높습니다.
이안 넬슨

1
누군가가 지금이를 찾는 경우, 속는 질문이 대답은 두 배로 불필요한 변환을 방지 하고 명확한 설명을 제공 할뿐만 아니라 오버 플로우 문제를 방지 할 수 있습니다.
ZX9

2
@IanNelson 더 일반적 경우 x로 나누어 y, y/x + 1너무 높은 하나가 될 것입니다.
하드 슈나이더

1
@ ZX9 아니요, 오버플로 문제를 피할 수는 없습니다. 이안 넬슨이 여기에 게시 한 것과 정확히 동일한 솔루션입니다.
user247702

답변:


478

우아한 해결책을 찾았습니다.

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

출처 : 숫자 변환, 롤랜드 백 하우스, 2001



30
Mr. 명백한 말 : recordsPerPage가 0이 아닌지 확인하십시오
Adam Gent

7
잘 했어, C #에 정수 한도가 없다고 믿을 수 없다.
gosukiwi

2
그렇습니다. 여기에서 나는 2017 년 중반에 훨씬 더 복잡한 접근법을 시도한 후이 위대한 대답을 가로 지르고 있습니다.
Mifo

1
Python과 같은 적절한 유클리드-분할 연산자가있는 언어의 경우 더 간단한 접근 방식이 있습니다 pageCount = -((-records) // recordsPerPage).
supercat

194

부동 소수점으로 변환하면 CPU 수준에서 시간이 많이 낭비되는 것처럼 보입니다.

이안 넬슨의 솔루션 :

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
두 솔루션은 레코드 수가 0 인 경우 동일한 pageCount를 반환하지 않습니다. 이 단순화 된 버전은 레코드 0에 대해 1 pageCount를 반환하는 반면 Roland Backhouse 버전은 0 pageCount를 반환합니다. 그것이 당신이 원하는 것이면 괜찮지 만 C # / Java 스타일 정수 나누기로 수행 할 때 두 방정식은 동일하지 않습니다.
이안 넬슨

10
넬슨 솔루션에서 단순화로 변경할 때 스캔하고 보드카가 누락 된 사람들을 위해 명확하게 편집하기 위해 괄호를 사용하여 단순화했습니다 ... int pageCount = ((records-1) / recordsPerPage) + 1;
데이브 헤이우드

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리턴 double또는 decimal입력 유형에 따라.
DanM7

15
그것은 매우 비효율적이기 때문에
Zar Shardan

6
비효율적이지만 이해하기 매우 쉽습니다. 페이지 수 계산은 일반적으로 요청 당 한 번 수행되므로 성능 손실을 측정 할 수 없습니다.
Jared Kells

1
이 "(dividend + (divisor-1)) / divisor;"보다 거의 읽기 쉽다. 또한 느리고 수학 라이브러리가 필요합니다.

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

2
위의 답변처럼 오버플로하지 않기 위해 +1 ... Math.ceiling의 경우 int를 두 배로 변환 한 다음 다시 다시 성능에 민감한 코드에서는 나쁜 생각입니다.
톱니 바퀴

3
C #에서 작동하지 않는 @RhysUlerich (int를 직접 bool로 변환 할 수 없음). rjmunro의 솔루션은 내가 생각하는 분기를 피하는 유일한 방법입니다.
smead

18

Ian이 제공 한 정수 수학 솔루션은 훌륭하지만 정수 오버플로 버그가 발생합니다. 변수가 all이라고 가정하면 수학 int을 사용 long하고 버그를 피하기 위해 솔루션을 다시 작성할 수 있습니다 .

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

경우 recordslong, 버그 남아 있습니다. 모듈러스 솔루션에는 버그가 없습니다.


4
제시된 시나리오에서 실제로이 버그에 부딪 칠 것이라고 생각하지 않습니다. 2 ^ 31 레코드는 페이지를 넘겨야 할 것이 많습니다.
rjmunro


@finnw : AFAICS, 해당 페이지에는 실제 사례가 없으며 이론적 시나리오에서 다른 사람이 버그를 발견했다는보고 만 있습니다.
rjmunro

5
그렇습니다. 버그를 지적하는 데 현혹되었습니다. 문제를 일으키지 않고 많은 버그가 영구적으로 존재할 수 있습니다. JDK가 바이너리 검색을 구현 ​​한 9 년 동안 같은 형식의 버그가 누군가가보고하기 전 ( googleresearch.blogspot.com/2006/06/… ) 문제는이 버그가 발생할 가능성에 관계없이 먼저 수정하지 않는 것입니다.
Brandon DuRette

4
또한 중요한 것은 페이징 된 요소의 수뿐만 아니라 페이지 크기이기도합니다. 따라서 라이브러리를 작성 중이고 누군가가 2 ^ 31-1 (Integer.MAX_VALUE)을 페이지 크기로 전달하여 페이지를 넘지 않기로 선택하면 버그가 트리거됩니다.
Brandon DuRette

7

브랜치를 피하는 Nick Berardi의 답변 변형 :

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

참고 : (-r >> (Integer.SIZE - 1))부호 비트는 r32 번 반복됩니다 ( >>연산자 의 부호 확장 덕분에 ). r이 값은 0이거나 음수이면 0으로, 양수이면 -1 r로 평가됩니다. 따라서 그것을 빼면 q1 if를 추가하는 효과가 records % recordsPerPage > 0있습니다.


4

레코드 == 0의 경우, rjmunro의 솔루션은 1을 제공합니다. 올바른 솔루션은 0입니다. 즉, 레코드가 0보다 크다는 것을 알고 있다면 (그리고 나는 우리가 모두 레코드 PerPage> 0이라고 가정한다고 확신한다면) rjmunro 솔루션은 정확한 결과를 제공합니다. 오버플로 문제가 없습니다.

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

모든 정수 수학 솔루션은 모든 부동 소수점 솔루션 보다 효율적 입니다.


이 방법은 성능 병목 현상이 아닐 수 있습니다. 그리고 그렇다면 지점의 비용도 고려해야합니다.
finnw

4

확장 방법이 필요한 경우 :

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

여기에 수표 (오버플로 DivideByZero등)가 없으며 원하는 경우 자유롭게 추가하십시오. 그건 그렇고, 메소드 호출 오버 헤드가 걱정되는 사람들에게는 이와 같은 간단한 함수가 어쨌든 컴파일러에 의해 인라인 될 수 있으므로 걱정할 부분은 없다고 생각합니다. 건배.

추신 : 당신도 이것을 알고 있으면 유용 할 것입니다 (나머지는 얻습니다).

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

1
이것은 올바르지 않습니다. 예를 들어 DivideUp(4, -2)0을 반환합니다 (-2 여야 함). 대답이나 함수 인터페이스에서 명확하지 않은 음이 아닌 정수에만 정확합니다 .
Thash

6
Thash, 왜 약간의 추가 검사를 추가하고 숫자가 음수이면 내 대답을 투표하지 않고 담요 문장을 잘못 작성하는 대신 실제로 유용한 것은 아닙니다. 케이스. 이미 다른 검사를 먼저 수행해야한다는 것을 이미 확인했습니다. "여기서는 검사 (오버플로, DivideByZero 등)가 없습니다 . 원하는 경우 추가하십시오 ."
Nicholas Petersen

1
질문은 "특히 페이지 매김 컨트롤 을 표시하는 방법에 대해 생각하고 있습니다"라고 언급 했으므로 음수는 어쨌든 범위를 벗어 났을 것입니다. 다시 한 번, 유용한 작업을 수행하고 원하는 경우 추가 확인을 제안하십시오. 팀 노력의 일원입니다.
Nicholas Petersen

무례하다는 뜻은 아니었고 그렇게한다면 미안합니다. 문제는 "정수 나누기 결과를 반올림하는 방법"이었습니다. 저자는 페이지 매김을 언급했지만 다른 사람들은 다른 요구를 가질 수 있습니다. 인터페이스에서 명확하지 않기 때문에 음수 정수 (예 : 다른 이름 또는 인수 유형)에서 작동하지 않는다는 것을 함수가 어떻게 든 반영하면 더 좋을 것이라고 생각합니다. 음의 정수로 작업하려면 예를 들어 피제수와 제수의 절대 값을 취하고 결과에 부호를 곱할 수 있습니다.
Thash

2

또 다른 대안은 mod () 함수 (또는 '%')를 사용하는 것입니다. 나머지가 0이 아닌 경우 나누기의 정수 결과를 증가시킵니다.


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

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

-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

부동 소수점 나누기를 한 다음 천장 함수를 사용하여 값을 다음 정수로 반올림합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.