development

C ++ : 가장 가까운 수의 배수로 올림

big-blog 2020. 6. 9. 07:44
반응형

C ++ : 가장 가까운 수의 배수로 올림


OK-기본 질문처럼 보일 정도로 여기에 게시하는 것이 거의 창피합니다 (누군가가 투표를 마치면 삭제하겠습니다).

이것이 C ++에서 여러 배수로 올림하는 올바른 방법입니까?

나는 이것과 관련된 다른 질문이 있다는 것을 알고 있지만 C ++에서 이것을 수행하는 가장 좋은 방법이 무엇인지 알고 싶어합니다.

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return numToRound;
 }

 int roundDown = ( (int) (numToRound) / multiple) * multiple;
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}

업데이트 : 죄송합니다. 의도를 분명히하지 못했습니다. 여기 몇 가지 예가 있어요.

roundUp(7, 100)
//return 100

roundUp(117, 100)
//return 200

roundUp(477, 100)
//return 500

roundUp(1077, 100)
//return 1100

roundUp(52, 20)
//return 60

roundUp(74, 30)
//return 90

음수에 대해 확실하지 않은 양수에 작동합니다. 정수 수학 만 사용합니다.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = numToRound % multiple;
    if (remainder == 0)
        return numToRound;

    return numToRound + multiple - remainder;
}

편집 : 다음은 음수로 작동하는 버전입니다. "위"라는 결과가 항상> = 입력 결과를 의미하는 경우.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = abs(numToRound) % multiple;
    if (remainder == 0)
        return numToRound;

    if (numToRound < 0)
        return -(abs(numToRound) - remainder);
    else
        return numToRound + multiple - remainder;
}

조건없이 :

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    return ((numToRound + multiple - 1) / multiple) * multiple;
}

음수의 경우 0에서 반올림하는 것과 같습니다.

편집 : 음수에도 작동하는 버전

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    int isPositive = (int)(numToRound >= 0);
    return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;
}

테스트


multiple2의 거듭 제곱 인 경우

int roundUp(int numToRound, int multiple) 
{
    assert(multiple && ((multiple & (multiple - 1)) == 0));
    return (numToRound + multiple - 1) & -multiple;
}

테스트


요인이 항상 긍정적일 때 작동합니다.

int round_up(int num, int factor)
{
    return num + factor - 1 - (num - 1) % factor;
}

편집 :이 반환합니다 round_up(0,100)=100. 를 반환하는 솔루션에 대해서는 아래의 Paul 의견을 참조하십시오 round_up(0,100)=0.


이것은 "n 비트가 몇 바이트를 차지하는지 어떻게 알 수 있습니까?"(A : (n 비트 + 7) / 8)의 문제를 일반화 한 것입니다.

int RoundUp(int n, int roundTo)
{
    // fails on negative?  What does that mean?
    if (roundTo == 0) return 0;
    return ((n + roundTo - 1) / roundTo) * roundTo; // edit - fixed error
}

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

조건을 뒤섞을 필요가 없습니다.


float roundUp(float number, float fixedBase) {
    if (fixedBase != 0 && number != 0) {
        float sign = number > 0 ? 1 : -1;
        number *= sign;
        number /= fixedBase;
        int fixedPoint = (int) ceil(number);
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

이것은 모든 플로트 번호 또는베이스에 적용됩니다 (예 : -4를 가장 가까운 6.75로 반올림 할 수 있음). 본질적으로 고정 소수점으로 변환하고 반올림 한 다음 다시 변환합니다. AWAY를 0에서 반올림하여 음수를 처리합니다. 또한 함수를 기본적으로 roundDown으로 변환하여 음수를 값으로 처리합니다.

int 특정 버전은 다음과 같습니다.

int roundUp(int number, int fixedBase) {
    if (fixedBase != 0 && number != 0) {
        int sign = number > 0 ? 1 : -1;
        int baseSign = fixedBase > 0 ? 1 : 0;
        number *= sign;
        int fixedPoint = (number + baseSign * (fixedBase - 1)) / fixedBase;
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

부정적인 입력 지원이 추가 된 플 린스의 대답은 어느 정도입니다.


짧고 달콤한 답변을 찾는 사람에게. 이것이 내가 사용한 것입니다. 부정에 대한 설명이 없습니다.

n - (n % r)

이전 요소를 반환합니다.

(n + r) - (n % r)

다음을 반환합니다. 이것이 누군가를 돕기를 바랍니다. :)


이것은 float, double, long, int 및 short에서 작동하는 템플릿 함수를 사용하는 최신 c ++ 접근법입니다 (그러나 사용되는 double 값으로 인해 long long 및 long double은 아닙니다).

#include <cmath>
#include <iostream>

template<typename T>
T roundMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

int main()
{
    std::cout << roundMultiple(39298.0, 100.0) << std::endl;
    std::cout << roundMultiple(20930.0f, 1000.0f) << std::endl;
    std::cout << roundMultiple(287399, 10) << std::endl;
}

그러나 당신은 쉽게에 대한 지원을 추가 할 수 있습니다 long long하고 long double아래와 같이 템플릿 특수화로 :

template<>
long double roundMultiple<long double>( long double value, long double multiple)
{
    if (multiple == 0.0l) return value;
    return std::round(value/multiple)*multiple;
}

template<>
long long roundMultiple<long long>( long long value, long long multiple)
{
    if (multiple == 0.0l) return value;
    return static_cast<long long>(std::round(static_cast<long double>(value)/static_cast<long double>(multiple))*static_cast<long double>(multiple));
}

반올림 할 함수를 만들려면 std::ceil및를 반올림하여 사용하십시오 std::floor. 위의 예는을 사용하여 반올림 std::round합니다.

아래와 같이 "라운드 업"또는 "라운드 실링"템플릿 기능으로 잘 알려져 있습니다.

template<typename T>
T roundCeilMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::ceil(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

아래와 같이 "라운드 다운"또는 "라운드 플로어"템플릿 기능으로 잘 알려져 있습니다.

template<typename T>
T roundFloorMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::floor(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

우선, 오류 조건 (multiple == 0)은 아마도 반환 값을 가져야합니다. 뭐? 모르겠어요 아마도 예외를 던지고 싶을 수도 있습니다. 그러나 아무것도 돌려주는 것은 위험하지 않습니다.

둘째, numToRound가 이미 배수가 아닌지 확인해야합니다. 그렇지 않으면에 추가 multipleroundDown오답이 표시됩니다.

셋째, 캐스트가 잘못되었습니다. numToRound정수로 캐스트 했지만 이미 정수입니다. 나누기 전에 두 배로 캐스팅하고 곱셈 후에 다시 int로 캐스팅해야합니다.

마지막으로 음수로 무엇을 원하십니까? 반올림 "위"는 0으로 반올림하거나 (양수와 같은 방향으로 반올림) 또는 0에서 멀어짐 ( "더 큰"음수)을 의미 할 수 있습니다. 또는 신경 쓰지 않을 수도 있습니다.

다음은 처음 세 가지 수정 사항이있는 버전이지만 부정적인 문제는 다루지 않습니다.

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 else if(numToRound % multiple == 0)
 {
  return numToRound
 }

 int roundDown = (int) (( (double) numToRound / multiple ) * multiple);
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}

2의 거듭 제곱으로 반올림 :

누군가가 2의 거듭 제곱의 가장 가까운 배수로 반올림 한 양수에 대한 솔루션이 필요한 경우를 대비하여 (내가 여기서 끝났기 때문에) :

// number: the number to be rounded (ex: 5, 123, 98345, etc.)
// pow2:   the power to be rounded to (ex: to round to 16, use '4')
int roundPow2 (int number, int pow2) {
    pow2--;                     // because (2 exp x) == (1 << (x -1))
    pow2 = 0x01 << pow2;

    pow2--;                     // because for any
                                //
                                // (x = 2 exp x)
                                //
                                // subtracting one will
                                // yield a field of ones
                                // which we can use in a
                                // bitwise OR

    number--;                   // yield a similar field for
                                // bitwise OR
    number = number | pow2;
    number++;                   // restore value by adding one back

    return number;
}

입력 번호가 이미 여러 개인 경우 입력 번호는 동일하게 유지됩니다.

다음은 GCC가 제공하는 x86_64 출력 -O2또는 -Os(9Sep2013 Build-godbolt GCC online)입니다.

roundPow2(int, int):
    lea ecx, [rsi-1]
    mov eax, 1
    sub edi, 1
    sal eax, cl
    sub eax, 1
    or  eax, edi
    add eax, 1
    ret

각 C 코드 줄은 어셈블리의 해당 줄과 완벽하게 일치합니다. http://goo.gl/DZigfX

각 명령어는 매우 빠르 므로 기능도 매우 빠릅니다. 코드가 너무 작고 빠르기 때문에 코드를 inline사용할 때이 기능 이 유용 할 수 있습니다 .


신용:


나는 사용하고있다 :

template <class _Ty>
inline _Ty n_Align_Up(_Ty n_x, _Ty n_alignment)
{
    assert(n_alignment > 0);
    //n_x += (n_x >= 0)? n_alignment - 1 : 1 - n_alignment; // causes to round away from zero (greatest absolute value)
    n_x += (n_x >= 0)? n_alignment - 1 : -1; // causes to round up (towards positive infinity)
    //n_x += (_Ty(-(n_x >= 0)) & n_alignment) - 1; // the same as above, avoids branch and integer multiplication
    //n_x += n_alignment - 1; // only works for positive numbers (fastest)
    return n_x - n_x % n_alignment; // rounds negative towards zero
}

그리고 2의 거듭 제곱 :

template <class _Ty>
bool b_Is_POT(_Ty n_x)
{
    return !(n_x & (n_x - 1));
}

template <class _Ty>
inline _Ty n_Align_Up_POT(_Ty n_x, _Ty n_pot_alignment)
{
    assert(n_pot_alignment > 0);
    assert(b_Is_POT(n_pot_alignment)); // alignment must be power of two
    -- n_pot_alignment;
    return (n_x + n_pot_alignment) & ~n_pot_alignment; // rounds towards positive infinity (i.e. negative towards zero)
}

음수 값을 모두 0으로 반올림하면 (모든 값에 대해 양의 무한대로 반올림 됨) 서명 된 오버플로 (C / C ++에서 정의되지 않음)에 의존하지 않습니다.

이것은 다음을 제공합니다.

n_Align_Up(10, 100) = 100
n_Align_Up(110, 100) = 200
n_Align_Up(0, 100) = 0
n_Align_Up(-10, 100) = 0
n_Align_Up(-110, 100) = -100
n_Align_Up(-210, 100) = -200
n_Align_Up_POT(10, 128) = 128
n_Align_Up_POT(130, 128) = 256
n_Align_Up_POT(0, 128) = 0
n_Align_Up_POT(-10, 128) = 0
n_Align_Up_POT(-130, 128) = -128
n_Align_Up_POT(-260, 128) = -256

int 디비전이 올바른 결과를 낼 수 있다는 것을 알지 못하면 float로 캐스팅하고 ceil ()을 사용하는 것이 더 안전합니다.


int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

C ++은 각 숫자를 반올림하므로 0.5를 추가하면 (1.5 인 경우 2) 1.49는 1.99이므로 1입니다.

편집-죄송합니다 반올림하고 싶지 않았다 + 0.5 대신 ceil () 메서드를 사용하는 것이 좋습니다


글쎄, 당신이하고 싶은 일을 정말로 이해하지 못하기 때문에, 라인

int roundUp = roundDown + multiple;
int roundCalc = roundUp;
return (roundCalc); 

확실히 단축 될 수

int roundUp = roundDown + multiple;
return roundUp;

이것이 도움이 될 수 있습니다.

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}

항상 반올림

int alwaysRoundUp(int n, int multiple)
{
    if (n % multiple != 0) {
        n = ((n + multiple) / multiple) * multiple;

        // Another way
        //n = n - n % multiple + multiple;
    }

    return n;
}

alwaysRoundUp (1, 10)-> 10

alwaysRoundUp (5, 10)-> 10

alwaysRoundUp (10, 10)-> 10


항상 반올림

int alwaysRoundDown(int n, int multiple)
{
    n = (n / multiple) * multiple;

    return n;
}

alwaysRoundDown (1, 10)-> 0

alwaysRoundDown (5, 10)-> 0

alwaysRoundDown (10, 10)-> 10


정상적인 방법으로 반올림

int normalRound(int n, int multiple)
{
    n = ((n + multiple/2)/multiple) * multiple;

    return n;
}

normalRound (1, 10)-> 0

normalRound (5, 10)-> 10

normalRound (10, 10)-> 10


2의 거듭 제곱 인 가장 가까운 배수로 반올림

unsigned int round(unsigned int value, unsigned int multiple){
    return ((value-1u) & ~(multiple-1u)) + multiple;
}

이것은 원하는 반올림 증분이 2의 거듭 제곱 인 캐시 라인을 따라 할당 할 때 유용 할 수 있지만 결과 값은 그 배수 만 필요합니다. gcc함수의 본체와 분할 또는 분기 8 개 어셈블리 명령어를 생성한다.

round(  0,  16) ->   0
round(  1,  16) ->  16
round( 16,  16) ->  16
round(257, 128) -> 384 (128 * 3)
round(333,   2) -> 334

위에 게시 된 것과 다소 비슷한 알고리즘을 발견했습니다.

int [(| x | + n-1) / n] * [(nx) / | x |], 여기서 x는 사용자 입력 값이고 n은 사용중인 배수입니다.

모든 값 x에서 작동합니다. 여기서 x는 정수입니다 (양수 또는 음수, 0 포함). C ++ 프로그램을 위해 특별히 작성했지만 기본적으로 모든 언어로 구현할 수 있습니다.


음수 numToRound의 경우 :

이 작업을 수행하는 것이 쉽지만 표준 모듈로 % 연산자는 예상과 같이 음수를 처리하지 않습니다. 예를 들어 -14 % 12 = -2이고 10이 아닙니다. 가장 먼저해야 할 일은 음수를 반환하지 않는 모듈로 연산자를 얻는 것입니다. 그러면 올림은 정말 간단합니다.

public static int mod(int x, int n) 
{
    return ((x % n) + n) % n;
}

public static int roundUp(int numToRound, int multiple) 
{
    return numRound + mod(-numToRound, multiple);
}

이것이 내가 할 일입니다.

#include <cmath>

int roundUp(int numToRound, int multiple)
{
    // if our number is zero, return immediately
   if (numToRound == 0)
        return multiple;

    // if multiplier is zero, return immediately
    if (multiple == 0)
        return numToRound;

    // how many times are number greater than multiple
    float rounds = static_cast<float>(numToRound) / static_cast<float>(multiple);

    // determine, whether if number is multiplier of multiple
    int floorRounds = static_cast<int>(floor(rounds));

    if (rounds - floorRounds > 0)
        // multiple is not multiplier of number -> advance to the next multiplier
        return (floorRounds+1) * multiple;
    else
        // multiple is multiplier of number -> return actual multiplier
        return (floorRounds) * multiple;
}

코드가 최적은 아니지만 건조 성능보다 깨끗한 코드를 선호합니다.


int roundUp (int numToRound, int multiple)
{
  return multiple * ((numToRound + multiple - 1) / multiple);
}

이기는 하지만:

  • 음수에는 작동하지 않습니다
  • numRound + 다중 오버플로 인 경우 작동하지 않습니다

대신 부호없는 정수를 사용하는 것이 좋습니다. 오버플로 동작을 정의했습니다.

배수 == 0이라는 예외가 발생하지만 어쨌든 잘 정의 된 문제는 아닙니다.


씨:

int roundUp(int numToRound, int multiple)
{
  return (multiple ? (((numToRound+multiple-1) / multiple) * multiple) : numToRound);
}

~ / .bashrc의 경우 :

roundup()
{
  echo $(( ${2} ? ((${1}+${2}-1)/${2})*${2} : ${1} ))
}

x이미 여러 배인 경우 나머지의 추가를 무효화하기 위해 모듈러스 조합을 사용합니다 .

int round_up(int x, int div)
{
    return x + (div - x % div) % div;
}

우리는 나머지의 역수를 구한 다음, 제수 자체가 있다면 제수를 사용하여 다시 무효화하는 계수를 추가 x합니다.

round_up(19, 3) = 21

다음은 OP의 제안과 다른 사람들이 제시 한 예를 기반으로 한 솔루션입니다. 대부분의 사람들이 음수를 처리하기 위해 찾고 있었기 때문에이 솔루션은 특별한 기능 (예 : abs 등)을 사용하지 않고 바로 그 기능을 수행합니다.

모듈러스를 피하고 나눗셈을 대신 사용하면 음수가 반올림되지만 자연스런 결과입니다. 반올림 된 버전이 계산 된 후에는 음수 또는 양수 방향으로 반올림하는 데 필요한 수학이 수행됩니다.

또한 아무것도 계산하는 데 특별한 기능이 사용되지 않으므로 속도가 약간 향상됩니다.

int RoundUp(int n, int multiple)
{
    // prevent divide by 0 by returning n
    if (multiple == 0) return n;

    // calculate the rounded down version
    int roundedDown = n / multiple * multiple;

    // if the rounded version and original are the same, then return the original
    if (roundedDown == n) return n;

    // handle negative number and round up according to the sign
    // NOTE: if n is < 0 then subtract the multiple, otherwise add it
    return (n < 0) ? roundedDown - multiple : roundedDown + multiple;
}

나는 이것이 당신을 도울 것이라고 생각합니다. C로 아래 프로그램을 작성했습니다.

# include <stdio.h>
int main()
{
  int i, j;
  printf("\nEnter Two Integers i and j...");
  scanf("%d %d", &i, &j);
  int Round_Off=i+j-i%j;
  printf("The Rounded Off Integer Is...%d\n", Round_Off);
  return 0;
}

/// Rounding up 'n' to the nearest multiple of number 'b'.
/// - Not tested for negative numbers.
/// \see http://stackoverflow.com/questions/3407012/
#define roundUp(n,b) ( (b)==0 ? (n) : ( ((n)+(b)-1) - (((n)-1)%(b)) ) )

/// \c test->roundUp().
void test_roundUp() {   
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( (n)%(b)==0 ? n : (n)+(b)-(n)%(b) ) )
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( ((n + b - 1) / b) * b ) )

    // no_roundUp(n,b) ( (n)%(b)==0 ? n : (b)*( (n)/(b) )+(b) )
    // no_roundUp(n,b) ( (n)+(b) - (n)%(b) )

if (true) // couldn't make it work without (?:)
{{  // test::roundUp()
    unsigned m;
                { m = roundUp(17,8); } ++m;
    assertTrue( 24 == roundUp(17,8) );
                { m = roundUp(24,8); }
    assertTrue( 24 == roundUp(24,8) );

    assertTrue( 24 == roundUp(24,4) );
    assertTrue( 24 == roundUp(23,4) );
                { m = roundUp(23,4); }
    assertTrue( 24 == roundUp(21,4) );

    assertTrue( 20 == roundUp(20,4) );
    assertTrue( 20 == roundUp(19,4) );
    assertTrue( 20 == roundUp(18,4) );
    assertTrue( 20 == roundUp(17,4) );

    assertTrue( 17 == roundUp(17,0) );
    assertTrue( 20 == roundUp(20,0) );
}}
}

이것은 양의 정수를 찾고있는 결과를 얻습니다.

#include <iostream>
using namespace std;

int roundUp(int numToRound, int multiple);

int main() {
    cout << "answer is: " << roundUp(7, 100) << endl;
    cout << "answer is: " << roundUp(117, 100) << endl;
    cout << "answer is: " << roundUp(477, 100) << endl;
    cout << "answer is: " << roundUp(1077, 100) << endl;
    cout << "answer is: " << roundUp(52,20) << endl;
    cout << "answer is: " << roundUp(74,30) << endl;
    return 0;
}

int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    }
    int result = (int) (numToRound / multiple) * multiple;
    if (numToRound % multiple) {
        result += multiple;
    } 
    return result;
}

출력은 다음과 같습니다.

answer is: 100
answer is: 200
answer is: 500
answer is: 1100
answer is: 60
answer is: 90

이것은 나를 위해 작동하지만 부정적인 것을 처리하려고하지 않았습니다

public static int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    } else if (numToRound % multiple == 0) {
    return numToRound;
    }

    int mod = numToRound % multiple;
    int diff = multiple - mod;
    return numToRound + diff;
}

참고 URL : https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number

반응형