C ++: Làm tròn đến bội số gần nhất của một số


168

OK - Tôi gần như lúng túng khi đăng bài này ở đây (và tôi sẽ xóa nếu có ai bỏ phiếu để đóng) vì đây có vẻ là một câu hỏi cơ bản.

Đây có phải là cách chính xác để làm tròn lên bội số của C ++ không?

Tôi biết có những câu hỏi khác liên quan đến vấn đề này nhưng tôi đặc biệt quan tâm để biết đâu là cách tốt nhất để làm điều này trong 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);
}

Cập nhật: Xin lỗi tôi có thể không làm rõ ý định. Dưới đây là một số ví dụ:

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

3
Bạn có một lỗi trong logic của bạn - giả sử tôi muốn làm tròn số 4 lên tới bội số gần nhất của 2. roundDown = (4/2) * 2 = 4; roundUp = 4 + 2; so roundCalc = 6. Tôi giả sử rằng bạn muốn trả về 4 trong trường hợp đó.
Niki Yoshiuchi

điều này không hoạt động cho roundUp (30,30). Nó đưa ra 60 câu trả lời, nó vẫn sẽ trả lời 30 câu trả lời ..
bsobaid

@bsobaid: Kiểm tra câu trả lời của tôi ở phía dưới. Nó đơn giản hơn một chút so với các giải pháp khác ở đây, mặc dù những giải pháp đó cũng hoạt động
Niklas B.

3
Các trường hợp thử nghiệm của bạn là các ví dụ thiếu rõ ràng liên quan đến các số âm, các trường hợp phân chia chính xác, các trường hợp phân chia gần như chính xác và các trường hợp các số rất gần giới hạn của phạm vi int.

1
Robben_Ford_Fan_boy, Chỉnh sửa với câu trả lời bạn đã thực hiện sẽ bị xóa. Nếu nó khác với câu trả lời, bạn có thể đăng câu trả lời của riêng bạn. Vì nó đứng, câu trả lời đó có vấn đề nên được giải quyết trong phần câu trả lời.
chux - Phục hồi Monica

Câu trả lời:


161

Điều này làm việc cho số dương, không chắc chắn về tiêu cực. Nó chỉ sử dụng toán học số nguyên.

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

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

    return numToRound + multiple - remainder;
}

Chỉnh sửa: Đây là phiên bản hoạt động với số âm, nếu bằng cách "lên", bạn có nghĩa là kết quả luôn luôn> = đầu vào.

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

+1 Theo tôi, chắc chắn là giải pháp tốt nhất và dễ đọc nhất.
Robben_Ford_Fan_boy

1
Thêm vào if(number<0){ multiple = multiple*(-1); }lúc bắt đầu làm tròn số âm theo đúng hướng
Josh

4
@Josh: Tại sao sử dụng phép nhân? if(number<0) multiple = -multipledễ dàng hơn
md5

điều này không hoạt động cho roundUp (30,30). Nó cho 60 là câu trả lời, nó vẫn nên trả lời 30.
bsobaid

@bsobaid không thể. Các if (remainder == 0)xét nghiệm nên chăm sóc trường hợp đó. Nó hoạt động với tôi: ideone.com/Waol7B
Mark Ransom

112

Không có điều kiện:

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

Điều này hoạt động như làm tròn từ 0 cho số âm

EDIT: Phiên bản hoạt động với số âm

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

Xét nghiệm


Nếu multiplelà sức mạnh bằng 2 (nhanh hơn ~ 3,7 lần http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80 )

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

Xét nghiệm


24
+1 cho sức mạnh của 2 phiên bản. Rất hữu ích vì nó hoàn toàn tránh được chi phí nhân, chia hoặc modulo.
Nikos C.

Bạn có chắc chắn rằng các thuật toán này không có điều kiện tiên quyết? Còn số âm thì sao? Hành vi dường như không được xác định trong tiền C ++ 11 .
cubuspl42

> Còn số âm thì sao? Theo mô tả, điều này hoạt động cho các số âm như làm tròn từ số không.
KindDragon

Tôi đọc "làm tròn lên" có nghĩa là làm tròn đến vô cực tích cực, không làm tròn từ số không.

8
Lưu ý rằng & ~(x - 1)giống như & -xđối với số học bổ sung của hai.
Todd Lehman

39

Điều này hoạt động khi yếu tố sẽ luôn tích cực:

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

Chỉnh sửa: Điều này trở lại round_up(0,100)=100. Vui lòng xem bình luận của Paul dưới đây để biết giải pháp trả về round_up(0,100)=0.


1
Có vẻ là trường hợp ngắn nhất xử lý trường hợp 'đã là nhiều'.
harningt

1
Giải pháp tốt nhất về số lượng các hoạt động tốn kém. Nó chỉ sử dụng một nhà tù duy nhất và không nhân lên
Niklas B.

3
round_up (0, 100) == 100 thay vì 0 như trong câu trả lời được chấp nhận
Gregory

7
Có nên không num + factor - 1 - (num + factor - 1) % factor?
Paul

6
num - 1 - (num - 1) % factor + factorthực hiện tính toán tương tự mà không có nguy cơ tràn số nguyên.

24

Đây là một khái quát của vấn đề "làm thế nào để tôi tìm ra bao nhiêu byte n bit sẽ mất? (A: (n bit + 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
}

1
Điều này không làm tròn đến bội số tiếp theo của một số.
aaaa bbbb

7
Tôi thích giải pháp này vì nếu roundTo sẽ là lũy thừa 2, bạn có thể loại bỏ / và * và kết thúc không có gì ngoài các thao tác rẻ tiền (x = roundTo - 1; return (n + x) & ~ x;)
Trejkaz

@Trejkaz không. Nó phải (x = roundTo - 1; return (n+x)&~roundTo;)giống như trong câu trả lời của tôi
KindDragon

@KindDragon tạo ra kết quả sai cho tôi, nhưng nếu tôi sửa nó thành ~ x thay vì ~ roundTo, tôi sẽ nhận được kết quả như mong đợi. Trên Java 8 nào.
Trejkaz

@KindDragon: Mặt nạ AND cần phải 0xFFF...000, không phải 0xFFF7FFFhoặc một cái gì đó, vì vậy bạn muốn phủ định bổ sung 2 ( -trừ đi) trên lũy thừa 2 hoặc lật bit trên một ít hơn 2 (nghịch đảo bổ sung của một , ~: dấu ngã không trừ). Vậy (n+x) & ~xhay (n-roundTo+1) & -roundTo.
Peter Cordes

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

Và không cần phải loay hoay với điều kiện


11

Đối với bất cứ ai tìm kiếm một câu trả lời ngắn gọn và ngọt ngào. Đây là những gì tôi đã sử dụng. Không có kế toán cho tiêu cực.

n - (n % r)

Điều đó sẽ trả lại các yếu tố trước đó.

(n + r) - (n % r)

Sẽ trở lại tiếp theo. Hy vọng điều này sẽ giúp được ai đó. :)


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

Điều này hoạt động cho bất kỳ số float hoặc cơ sở (ví dụ: bạn có thể làm tròn -4 đến 6,75 gần nhất). Về bản chất, nó đang chuyển đổi thành điểm cố định, làm tròn ở đó, sau đó chuyển đổi trở lại. Nó xử lý các phủ định bằng cách làm tròn AWAY từ 0. Nó cũng xử lý một vòng âm thành giá trị bằng cách chủ yếu biến hàm thành roundDown.

Một phiên bản int cụ thể trông như:

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

Đó là câu trả lời nhiều hay ít của plinth, với sự hỗ trợ đầu vào tiêu cực được thêm vào.


Tôi đã thử nghiệm float roundUp với double, nó hoạt động với tôi. Thực sự giải quyết vấn đề của tôi.
Ashif

1
Điều gì về double round(double value, double multiple) { double sign = value; multiple = std::copysign(multiple, 1.0); value = std::copysign(value, 1.0); return std::copysign(multiple * std::ceil(value / multiple), sign); }Hoặc hoán đổi trần cho vòng để có được làm tròn.
Troyseph

8

Đây là cách tiếp cận c ++ hiện đại sử dụng hàm mẫu đang hoạt động cho float, double, long, int và short (nhưng không dài và dài gấp đôi vì các giá trị kép được sử dụng).

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

Nhưng bạn có thể dễ dàng thêm hỗ trợ cho long longlong doublevới chuyên môn mẫu như dưới đây:

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

Để tạo các chức năng để làm tròn, sử dụng std::ceilvà luôn luôn làm tròn sử dụng std::floor. Ví dụ của tôi từ trên là làm tròn bằng cách sử dụng std::round.

Tạo chức năng mẫu "làm tròn" hay còn gọi là chức năng mẫu "trần tròn" như dưới đây:

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

Tạo chức năng mẫu "làm tròn xuống" hay còn gọi là chức năng mẫu "sàn tròn" như dưới đây:

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

1
cộng với 1, mặc dù một số người có thể thấy nó trở nên hợp lý hơn khi trả về 0 khi mulitple == 0
stijn

3
Coi chừng, vì chuyển đổi int64_t thành gấp đôi có thể bị mất, do đó, nó không hoàn toàn giống như kiểu chung như nó có thể xuất hiện.
Adrian McCarthy

@AdrianMcCarthy Có, bạn phải tạo các chuyên môn mẫu chính xác như được hiển thị ở trên. Như bạn có thể thấy, tôi thực hiện hai chức năng bổ sung cho long longlong double. Điều tương tự phải được thực hiện cho hai chức năng khác rõ ràng.
Flovdis

Tôi nghĩ rằng đây là chậm nhất trong tất cả nhưng nó sẽ không phải là. Tất cả những gì bạn cần làm là std :: enable_if_t và thực hiện hai nhánh cho số nguyên và số float. Bạn cũng có thể sử dụng số_limits tốt hơn và xem liệu lớp phủ có đủ lớn để thực sự phù hợp với giá trị hay không. Điều đó sẽ thêm an toàn.
con lợn

5

Trước hết, điều kiện lỗi của bạn (bội == 0) có thể có giá trị trả về. Gì? Tôi không biết. Có lẽ bạn muốn ném một ngoại lệ, đó là tùy thuộc vào bạn. Nhưng, trở về không có gì là nguy hiểm.

Thứ hai, bạn nên kiểm tra xem numToRound chưa phải là bội số. Nếu không, khi bạn thêm multiplevào roundDown, bạn sẽ nhận được câu trả lời sai.

Thứ ba, dàn diễn viên của bạn sai. Bạn numToRoundchuyển sang một số nguyên, nhưng nó đã là một số nguyên. Bạn cần phải tăng gấp đôi trước khi chia và trở lại int sau khi nhân.

Cuối cùng, bạn muốn gì cho số âm? Làm tròn "lên" có thể có nghĩa là làm tròn thành số không (làm tròn theo cùng hướng với số dương) hoặc cách xa số 0 (số âm "lớn hơn"). Hoặc, có thể bạn không quan tâm.

Đây là phiên bản có ba bản sửa lỗi đầu tiên, nhưng tôi không giải quyết vấn đề tiêu cực:

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

@Peter phải không? Tôi giả định rằng int / intsẽ trả về một int, đó không phải là những gì chúng ta muốn.
Mike Caron

int / int thực sự trả về một int, nhưng đó chính xác là những gì bạn muốn. Ví dụ: numToRound = 7, bội = 3. 7/3 = 2.
Peter Ruderman

4

Vòng tới sức mạnh của hai:

Chỉ trong trường hợp bất kỳ ai cũng cần một giải pháp cho các số dương được làm tròn thành bội số gần nhất của lũy thừa hai (vì đó là cách tôi kết thúc ở đây):

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

Số đầu vào sẽ giữ nguyên nếu nó đã là bội số.

Đây là đầu ra x86_64 mà GCC cung cấp với -O2hoặc -Os(9Sep2013 Build - godbolt GCC trực tuyến):

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

Mỗi dòng mã C tương ứng hoàn hảo với dòng của nó trong cụm: http://goo.gl/DZigfX

Mỗi hướng dẫn đó đều cực kỳ nhanh , do đó chức năng cũng cực kỳ nhanh. Vì mã rất nhỏ và nhanh, nên nó có thể hữu ích cho inlinehàm khi sử dụng nó.


Tín dụng:


1
Chính xác những gì tôi đang tìm kiếm. Cảm ơn!
kiyo

1
int roundUpPow2 (int num, int pow2) {return num + (pow2 - 1) & ~ (pow2 - 1); } nhanh hơn khoảng 30% và dễ sử dụng hơn (bạn vượt qua 16 chứ không phải 4 để làm tròn tới bội số tiếp theo của 16.
Axel Rietschin

3

Tôi đang sử dụng:

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
}

và cho quyền hạn của hai:

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

Lưu ý rằng cả hai giá trị âm tròn này đều bằng 0 (có nghĩa là làm tròn đến vô cực dương cho tất cả các giá trị), cả hai đều không phụ thuộc vào tràn tràn đã ký (không được xác định trong C / C ++).

Điều này mang lại:

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

Tôi đã sử dụng của bạn n_Align_Up_POTkể từ khi tôi nhìn thấy nó trong lớp TList của Delphi. Nó có những hạn chế, như căn chỉnh (bội số) là lũy thừa của 2, nhưng điều đó hiếm khi xảy ra vì tôi chủ yếu sử dụng nó để nhận / kiểm tra căn chỉnh chính xác cho SMID. Nó là tuyệt vời và dường như không nhiều người biết về nó.
dùng1593842

2

Có lẽ an toàn hơn để đúc thành phao và sử dụng ceil () - trừ khi bạn biết rằng phép chia int sẽ tạo ra kết quả chính xác.


1
Lưu ý rằng gấp đôi chỉ có thể chứa 54 bit có ý nghĩa và trên các máy dựa trên x86. Nếu bạn có số nguyên 64 bit, cuối cùng nó sẽ thất bại.
con lợn

Nhân đôi tiêu chuẩn của IEEE754 không thể nhưng x64 cpus có điểm nổi nội bộ 80 bit nên các thao tác trên một số duy nhất là đáng tin cậy
Martin Beckett

1
Trong khi đó là sự thật, bạn có rất ít quyền kiểm soát đối với việc làm tròn từ C / C ++. Nó phụ thuộc vào các cài đặt từ điều khiển và nó thực sự có thể làm tròn đến dưới 80 bit. Ngoài ra, bạn có SSE và các bộ hướng dẫn SIMD khác không có trung gian mở rộng như vậy (trình biên dịch vector hóa có thể dễ dàng sử dụng chúng).
con lợn

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

C ++ làm tròn mỗi số xuống, vì vậy nếu bạn thêm 0,5 (nếu là 1,5 thì sẽ là 2) nhưng 1,49 sẽ là 1,99 do đó 1.

EDIT - Xin lỗi đã không thấy bạn muốn làm tròn, tôi sẽ đề nghị sử dụng phương thức ceil () thay vì +0,5


2

tốt cho một điều, vì tôi không thực sự hiểu những gì bạn muốn làm, các dòng

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

chắc chắn có thể được rút ngắn để

int roundUp = roundDown + multiple;
return roundUp;

2

có thể điều này có thể giúp:

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

Tại sao sử dụng phân chia sàn và số nguyên? Không có gì để sàn. Nếu nó là gấp đôi, ít nhất bạn có thể kế thừa việc xử lý các giá trị âm.
con lợn

2

Luôn luôn làm tròn

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


Luôn luôn làm tròn xuống

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

    return n;
}

alwaysRoundDown (1, 10) -> 0

alwaysRoundDown (5, 10) -> 0

alwaysRoundDown (10, 10) -> 10


Làm tròn theo cách thông thường

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

    return n;
}

bình thường (1, 10) -> 0

bình thường (5, 10) -> 10

bình thường (10, 10) -> 10


2

Làm tròn đến bội số gần nhất có thể là lũy thừa của 2

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

Điều này có thể hữu ích khi phân bổ dọc theo các bộ đệm, trong đó mức tăng làm tròn bạn muốn là lũy thừa của hai, nhưng giá trị kết quả chỉ cần là bội số của nó. Trên gccthân của hàm này tạo ra 8 lệnh lắp ráp không có phân chia hoặc nhánh.

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

1

Tôi tìm thấy một thuật toán có phần giống với thuật toán được đăng ở trên:

int [(| x | + n-1) / n] * [(nx) / | x |], trong đó x là giá trị đầu vào của người dùng và n là bội số được sử dụng.

Nó hoạt động cho tất cả các giá trị x, trong đó x là một số nguyên (dương hoặc âm, bao gồm 0). Tôi đã viết nó đặc biệt cho một chương trình C ++, nhưng về cơ bản điều này có thể được thực hiện bằng bất kỳ ngôn ngữ nào.


1

Đối với numToRound âm:

Nó thực sự dễ dàng để làm điều này nhưng toán tử modulo% tiêu chuẩn không xử lý các số âm như người ta có thể mong đợi. Ví dụ -14% 12 = -2 chứ không phải 10. Điều đầu tiên cần làm là lấy toán tử modulo không bao giờ trả về số âm. Sau đó roundUp thực sự đơn giản.

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

Đây là những gì tôi sẽ làm:

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

Mã có thể không tối ưu, nhưng tôi thích mã sạch hơn hiệu năng khô.


Đúc intđể floatdễ mất độ chính xác và làm cho câu trả lời không chính xác.
chux - Phục hồi lại

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

Mặc du:

  • sẽ không làm việc cho số âm
  • sẽ không hoạt động nếu numRound + nhiều tràn

thay vào đó sẽ đề xuất sử dụng số nguyên không dấu, trong đó đã xác định hành vi tràn.

Bạn sẽ nhận được một ngoại lệ là nhiều == 0, nhưng dù sao đó cũng không phải là vấn đề được xác định rõ.


1

c:

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

và cho ~ / .bashrc của bạn:

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

1

Tôi sử dụng kết hợp mô-đun để vô hiệu hóa việc thêm phần còn lại nếu xđã là bội số:

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

Chúng tôi tìm thấy nghịch đảo của phần còn lại sau đó mô đun với phép chia một lần nữa để vô hiệu hóa nó nếu đó là chính số chia sau đó thêm x.

round_up(19, 3) = 21

1

Đây là giải pháp của tôi dựa trên đề xuất của OP và các ví dụ được đưa ra bởi mọi người khác. Vì hầu hết mọi người đều tìm kiếm nó để xử lý các số âm, giải pháp này thực hiện điều đó, mà không sử dụng bất kỳ chức năng đặc biệt nào, tức là abs và tương tự.

Bằng cách tránh mô đun và sử dụng phép chia thay vào đó, số âm là kết quả tự nhiên, mặc dù nó được làm tròn xuống. Sau khi tính toán phiên bản làm tròn xuống, sau đó nó thực hiện phép toán cần thiết để làm tròn, theo hướng tiêu cực hoặc tích cực.

Cũng lưu ý rằng không có chức năng đặc biệt nào được sử dụng để tính toán bất cứ điều gì, do đó, có một sự gia tăng tốc độ nhỏ ở đó.

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

Thất bại với RoundUp(INT_MIN, -1)như n / multipleinttràn.
chux - Tái lập Monica

1

Tôi nghĩ rằng điều này sẽ giúp bạn. Tôi đã viết chương trình dưới đây trong 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;
}

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

0

Điều này nhận được kết quả mà bạn đang tìm kiếm cho các số nguyên dương:

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

Và đây là kết quả đầu ra:

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

-1

Điều này làm việc cho tôi nhưng không cố gắng xử lý tiêu cực

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

-2

Dưới đây là một giải pháp siêu đơn giản để thể hiện khái niệm thanh lịch. Về cơ bản, nó là cho snaps lưới.

(mã giả)

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

bạn đã kiểm tra ý tưởng của bạn được gửi chưa? nó không đủ để đưa ra câu trả lời chính xác
yaodav
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.