簡體   English   中英

四舍五入到最接近的數字倍數

[英]Rounding up to the nearest multiple of a number

好的 - 我幾乎不好意思在這里發帖(如果有人投票關閉,我會刪除),因為這似乎是一個基本問題。

這是在 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;
}

這就像為負數從零舍入

編輯:也適用於負數的版本

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

測試


如果multiple是 2 的冪(快 3.7 倍http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80

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 + factor - 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;  
}

並且不需要亂七八糟的條件

這是使用模板函數的現代 C++ 方法,該函數適用於 float、double、long、int 和 short(但不適用於 long long 和 long double,因為使用了 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 longlong 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));
}

創建“round down”或更廣為人知的“round floor”模板函數,如下所示:

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

對於任何尋求簡短而甜蜜的答案的人。 這是我用的。 不考慮負數。

n - (n % r)

這將返回前一個因素。

(n + r) - (n % r)

下次還會再來。 希望這可以幫助某人。 :)

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)。 本質上它是轉換為定點,在那里四舍五入,然后轉換回來。 它通過從 0 舍入 AWAY 來處理負數。它還通過基本上將函數轉換為 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;
}

這或多或少是底座的答案,增加了負輸入支持。

首先,您的錯誤條件(multiple == 0)可能應該有一個返回值。 什么? 我不知道。 也許你想拋出一個異常,這取決於你。 但是,什么都不返回是危險的。

其次,您應該檢查 numToRound 是否已經不是倍數。 否則,當您將multiple添加到roundDown ,您會得到錯誤的答案。

第三,你的演員陣容是錯誤的。 您將numToRound轉換為整數,但它已經是整數。 您需要在除法之前轉換為 double ,並在乘法之后返回 int 。

最后,你想要什么負數? “向上”四舍五入可能意味着四舍五入到零(以與正數相同的方向四舍五入)或遠離零(“更大”的負數)。 或者,也許你不在乎。

這是包含前三個修復程序的版本,但我不處理負面問題:

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 的冪的倍數的解決方案(因為這就是我在這里結束的方式):

// 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提供的 x86_64 輸出(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
}

並為二的權力:

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

也許這可以幫助:

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

可能更安全地轉換為浮點數並使用 ceil() - 除非您知道 int 除法將產生正確的結果。

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

C++ 將每個數字向下舍入,因此如果添加 0.5(如果它是 1.5,它將是 2)但 1.49 將是 1.99 因此是 1。

編輯 - 抱歉沒有看到你想要四舍五入,我建議使用 ceil() 方法而不是 +0.5

一方面,因為我真的不明白你想做什么,線條

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

絕對可以縮短為

int roundUp = roundDown + multiple;
return roundUp;

四舍五入到恰好是 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

對於負 numToRound:

這樣做應該很容易,但標准的模 % 運算符並不能像人們期望的那樣處理負數。 例如 -14 % 12 = -2 而不是 10。首先要做的是獲得永遠不會返回負數的模運算符。 那么roundUp真的很簡單。

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 + 多次溢出將不起作用

建議改用無符號整數,它定義了溢出行為。

你會得到一個異常是 multiple == 0,但無論如何在這種情況下它不是一個明確定義的問題。

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

如果x已經是倍數,我使用模數的組合來抵消余數的添加:

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

我們找到余數的倒數,然后再次用除數取模以使其無效,如果它是除數本身,則添加x

round_up(19, 3) = 21

我發現了一種與上面發布的算法有些相似的算法:

int[(|x|+n-1)/n]*[(nx)/|x|],其中 x 是用戶輸入值,n 是使用的倍數。

它適用於所有值 x,其中 x 是一個整數(正數或負數,包括零)。 我是專門為 C++ 程序編寫的,但這基本上可以用任何語言實現。

這是我基於 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;
}

無限的可能性,僅適用於有符號整數:

n + ((r - n) % r)

/// 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

我認為這有效:

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

這對我有用,但沒有嘗試處理底片

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

這里有一個超級簡單的解決方案來展示優雅的概念。 它基本上用於網格捕捉。

(偽代碼)

nearestPos = Math.Ceil( numberToRound / multiple ) * multiple;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM