web-dev-qa-db-ja.com

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
150

これは正の数で機能しますが、負の数ではわかりません。整数演算のみを使用します。

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;
}
145
Mark Ransom

条件なし:

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

これは、ゼロから四捨五入のように機能します

編集:負の数でも機能するバージョン

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

テスト


multipleが2のべき乗の場合

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

テスト

96
KindDragon

これは、係数が常に正の場合に機能します。

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

編集:これはround_up(0,100)=100を返します。 round_up(0,100)=0を返すソリューションについては、以下のPaulのコメントを参照してください。

35
xlq

これは、「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
}
22
plinth
int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

条件をいじる必要はありません

14
doron
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;
}

これは多かれ少なかれ台座の答えであり、負の入力サポートが追加されています。

9
Dolphin

短くて甘い答えをお探しの方に。これは私が使用したものです。ネガを考慮しません。

n - (n % r)

それは前の要素を返します。

(n + r) - (n % r)

次を返します。これが誰かを助けることを願っています。 :)

7
aaron-bond

これは、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));
}
7
Flovdis

まず、エラー状態(複数== 0)にはおそらく戻り値が必要です。何?知りません。例外をスローしたいかもしれませんが、それはあなた次第です。しかし、何も返さないことは危険です。

次に、numToRoundが既に倍数になっていないことを確認する必要があります。それ以外の場合、multipleroundDownに追加すると、間違った答えが返されます。

第三に、キャストが間違っています。 numToRoundを整数にキャストしましたが、すでに整数です。除算の前にdoubleにキャストし、乗算後にintに戻す必要があります。

最後に、負の数に対して何が必要ですか? 「切り上げ」とは、ゼロへの丸め(正の数と同じ方向への丸め)、またはゼロから離れる(「より大きい」負の数)ことを意味します。または、気にしないかもしれません。

最初の3つの修正を含むバージョンを次に示しますが、マイナスの問題には対処しません。

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);
}
5
Mike Caron

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が-O2または-Os(2013年9月ビルド-godbolt GCCオンライン)で提供するx86_64出力を次に示します。

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すると便利です。


クレジット:

4
haneefmubarak

私は使用しています:

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)
}

これらの負の値は両方ともゼロに丸められ(すべての値が正の無限大に丸められます)、どちらも符号付きオーバーフロー(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
3
the swine

常に切り上げる

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
onmyway133

これが役立つ場合があります:

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

おそらくfloatにキャストしてceil()を使用する方が安全です-int除算が正しい結果を生成することがわかっている場合を除きます。

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

C++は各数値を切り捨てるため、0.5を追加すると(1.5の場合は2になります)、1.49は1.99になるため、1です。

編集-申し訳ありませんが、切り上げたいとは思わなかったので、+ 0.5の代わりにceil()メソッドを使用することをお勧めします

2
Michal Ciechan

たまたま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
2
Anne Quinn

1つには、あなたが何をしたいのか本当に理解していないので、行

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

間違いなく短縮できます

int roundUp = roundDown + multiple;
return roundUp;
2
Jesse Naugher

これは私がすることです:

#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;
}

コードは最適ではないかもしれませんが、ドライなパフォーマンスよりもクリーンなコードの方が好きです。

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

ただし:

  • 負の数では機能しません
  • numRound +複数のオーバーフローがある場合は機能しません

代わりに、オーバーフロー動作を定義した符号なし整数を使用することをお勧めします。

例外は複数== 0ですが、とにかく明確に定義された問題ではありません。

1
user3392484

xがすでに倍数の場合、剰余の加算を無効にするためにモジュラスの組み合わせを使用します。

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

余りの逆数を見つけ、それが除数自体である場合に除数を再び無効にするモジュラスを見つけ、xを追加します。

round_up(19, 3) = 21
1
Nick Bedford

上記の投稿に似たアルゴリズムを見つけました。

int [(| x | + n-1)/ n] * [(nx)/ | x |]、xはユーザー入力値、nは使用されている倍数です。

すべての値xに対して機能します。xは整数(ゼロを含む正または負)です。特にC++プログラム用に作成しましたが、これは基本的にどの言語でも実装できます。

1
Joshua Wade

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;
}
1
weatx

c:

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

および〜/ .bashrcの場合:

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

負の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);
}
1
user990343

これはあなたの役に立つと思います。以下のプログラムを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;
}
1
Neel

これは、正の整数を求めている結果を取得しています:

#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
0
Dave
/// 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) );
}}
}
0
Adolfo