C ++: ปัดเศษขึ้นเป็นพหุคูณใกล้เคียงที่สุดของตัวเลข


168

ตกลง - ฉันเกือบจะอายโพสต์ที่นี่ (และฉันจะลบถ้าใครโหวตให้ปิด) เพราะดูเหมือนว่าคำถามพื้นฐาน

นี่เป็นวิธีที่ถูกต้องในการปัดเศษขึ้นเป็นทวีคูณของตัวเลขใน 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

3
คุณมีข้อผิดพลาดในตรรกะของคุณ - สมมติว่าฉันต้องการปัดเศษ 4 ขึ้นเป็นพหุคูณที่ใกล้เคียงที่สุดของ 2 roundDown = (4/2) * 2 = 4; roundUp = 4 + 2; ดังนั้น roundCalc = 6. ฉันสมมติว่าคุณต้องการคืนค่า 4 ในกรณีนั้น
Niki Yoshiuchi

สิ่งนี้ใช้ไม่ได้กับ roundUp (30,30) มันทำให้ 60 เป็นคำตอบก็ยังควรให้ 30 เป็นคำตอบ ..
bsobaid

@bsobaid: ตรวจสอบคำตอบของฉันที่ด้านล่าง มันง่ายกว่าโซลูชันอื่นเล็กน้อยที่นี่ถึงแม้ว่าสิ่งเหล่านี้จะใช้ได้เช่นกัน
Niklas B.

3
กรณีทดสอบของคุณจะผงาดตัวอย่างที่เกี่ยวข้องกับตัวเลขเชิงลบกรณีที่ส่วนที่เป็นที่แน่นอนกรณีที่ส่วนที่เป็นเกือบแน่นอนและกรณีที่ขาดหายไปที่ตัวเลขที่มีมากอยู่ใกล้กับขีด จำกัด intของความหลากหลายของ

1
Robben_Ford_Fan_boy ควรลบการแก้ไขที่มีคำตอบที่คุณไป หากมันแตกต่างจากคำตอบที่ได้รับคุณสามารถโพสต์คำตอบของคุณเอง คำตอบนั้นมีปัญหาที่ควรแก้ไขในส่วนคำตอบ
chux - Reinstate Monica

คำตอบ:


161

วิธีนี้ใช้ได้กับจำนวนบวก แต่ไม่แน่ใจว่าจะเป็นค่าลบ มันใช้เลขจำนวนเต็มเท่านั้น

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

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

    return numToRound + multiple - remainder;
}

แก้ไข: นี่คือรุ่นที่ใช้งานได้กับจำนวนลบหากโดย "up" คุณหมายถึงผลลัพธ์ที่เสมอ> = อินพุต

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 ในความคิดของฉันแน่นอนทางออกที่ดีที่สุดและอ่านได้มากที่สุด
Robben_Ford_Fan_boy

1
บวกif(number<0){ multiple = multiple*(-1); }ที่จุดเริ่มต้นเพื่อปัดเศษตัวเลขที่เป็นลบในทิศทางที่ถูกต้อง
Josh

4
@Josh: ทำไมต้องใช้การคูณ if(number<0) multiple = -multipleง่ายกว่า
md5

สิ่งนี้ใช้ไม่ได้กับ roundUp (30,30) มันให้ 60 เป็นคำตอบ แต่ก็ควรให้ 30 เป็นคำตอบ
bsobaid

@bsobaid เป็นไปไม่ได้ การif (remainder == 0)ทดสอบควรดูแลกรณีนั้น มันใช้งานได้สำหรับฉัน: ideone.com/Waol7B
Mark Ransom

113

ไม่มีเงื่อนไข:

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

การทดสอบ


24
+1 สำหรับกำลังของรุ่น 2 มีประโยชน์มากเพราะหลีกเลี่ยงค่าใช้จ่ายในการคูณหน่วยงานหรือโมดูโลทั้งหมด
Nikos C.

คุณแน่ใจหรือไม่ว่าอัลกอริทึมเหล่านี้ไม่มีเงื่อนไขเบื้องต้น? แล้วตัวเลขลบล่ะ? พฤติกรรมที่ดูเหมือนว่าจะไม่ได้กำหนดใน pre-C
cubuspl42

> แล้วตัวเลขลบล่ะ? ตามที่อธิบายไว้วิธีนี้ใช้สำหรับตัวเลขลบเช่นปัดเศษออกจากศูนย์
KindDragon

ฉันอ่าน "การปัดเศษขึ้น" เป็นความหมายของการปัดเศษไปทางอินฟินิตี้บวกไม่ใช่ปัดเศษจากศูนย์

8
โปรดทราบว่า& ~(x - 1)เป็นเช่นเดียวกับ& -xเลขคณิตส่วนเติมของสอง
ทอดด์เลห์แมน

39

สิ่งนี้ทำงานได้เมื่อปัจจัยเป็นบวกเสมอ:

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

แก้ไข: ผลตอบแทนround_up(0,100)=100นี้ round_up(0,100)=0โปรดดูความคิดเห็นของพอลด้านล่างสำหรับการแก้ปัญหาที่ส่งกลับ


1
ดูเหมือนว่าจะเป็นกรณีที่สั้นที่สุดที่จัดการกับกรณีที่มีอยู่แล้ว 'หลายรายการ'
หารายได้

1
ทางออกที่ดีที่สุดในแง่ของจำนวนการดำเนินการค่าใช้จ่าย ใช้เพียงดิวิชันเดียวและไม่มีการคูณ
Niklas B.

3
round_up (0, 100) == 100 แทน 0 เช่นเดียวกับในคำตอบที่ได้รับการยอมรับ
Gregory

7
ไม่ควรnum + factor - 1 - (num + factor - 1) % factorหรือ
พอล

6
num - 1 - (num - 1) % factor + factorทำการคำนวณเดียวกันโดยไม่มีความเสี่ยงของจำนวนเต็มล้น

24

นี่เป็นข้อสรุปทั่วไปของปัญหา "ฉันจะทราบได้อย่างไรว่าต้องใช้จำนวนไบต์ n บิตเท่าใด (A: (n bits + 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
สิ่งนี้จะไม่ปัดเศษขึ้นเป็นผลคูณของจำนวนถัดไป
aaaa bbbb

7
ฉันชอบวิธีนี้เพราะถ้า roundTo จะเป็นกำลัง 2 คุณสามารถกำจัด / และ * และจบลงด้วยการดำเนินการที่ถูก (x = roundTo - 1; return (n + x) & ~ x;)
Trejkaz

@Trejkaz ไม่ มันควรจะ(x = roundTo - 1; return (n+x)&~roundTo;)เป็นในคำตอบของฉัน
KindDragon

@KindDragon ที่สร้างผลลัพธ์ที่ผิดสำหรับฉัน แต่ถ้าฉันแก้ไขให้พูด ~ x แทนที่จะเป็น ~ roundTo ฉันจะได้ผลลัพธ์ที่ต้องการ บน Java 8 ต่อไป
Trejkaz

@KindDragon: หน้ากาก AND ต้อง0xFFF...000ไม่ใช่0xFFF7FFFหรือบางอย่างดังนั้นคุณต้องการการปฏิเสธแบบสมบูรณ์ของ 2 ( -: ลบ) โดยใช้กำลัง 2 หรือบิตพลิกในหนึ่งที่น้อยกว่าพลัง 2 (เติมเต็มส่วนใดส่วนหนึ่ง~: tilde ไม่ลบ) ดังนั้นหรือ(n+x) & ~x (n-roundTo+1) & -roundTo
Peter Cordes

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

และไม่จำเป็นต้องยุ่งกับเงื่อนไข


11

สำหรับทุกคนที่กำลังมองหาคำตอบที่สั้นและหวาน นี่คือสิ่งที่ฉันใช้ ไม่มีการบัญชีสำหรับเชิงลบ

n - (n % r)

ที่จะส่งกลับปัจจัยก่อนหน้า

(n + r) - (n % r)

จะกลับมาอีก หวังว่านี่จะช่วยใครซักคน :)


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

วิธีนี้ใช้ได้กับเลขทศนิยมหรือฐานใด ๆ (เช่นคุณสามารถปัดเศษ -4 ถึง 6.75 ที่ใกล้ที่สุด) ในสาระสำคัญมันจะแปลงเป็นจุดคงที่ปัดเศษที่นั่นแล้วแปลงกลับ มันจัดการเชิงลบโดยการปัดเศษ AWAY จาก 0 นอกจากนี้ยังจัดการรอบลบให้เป็นค่าโดยการเปลี่ยนฟังก์ชั่นเป็น roundDown

รุ่นที่เฉพาะเจาะจงดูเหมือนว่า:

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

คำตอบของรูปสลักซึ่งมากหรือน้อยด้วยการสนับสนุนการป้อนข้อมูลเชิงลบเพิ่ม


ฉันได้ทดสอบ float roundUp code ด้วย double มันใช้งานได้สำหรับฉัน แก้ปัญหาของฉันได้จริงๆ
Ashif

1
สิ่งที่เกี่ยวกับ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); }หรือแลกเปลี่ยนเพดานสำหรับรอบที่จะได้รับการปัดเศษ
Troyseph

8

นี่เป็นวิธีการ c ++ ที่ทันสมัยโดยใช้ฟังก์ชั่นเทมเพลตซึ่งทำงานสำหรับ float, double, long, int และ short (แต่ไม่ใช่สำหรับ long long และ long double เนื่องจาก double values ​​ใช้แล้ว)

#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ตัวอย่างของฉันจากข้างต้นจะถูกปัดเศษใช้

สร้างฟังก์ชั่นเทมเพลต "round up" หรือรู้จักกันในชื่อ "round ceiling" ดังที่แสดงด้านล่าง:

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

1
บวก 1 ถึงแม้ว่าบางคนอาจพบว่าเป็นกันเองมากกว่าที่จะส่งคืน 0 เมื่อ mulitple == 0
stijn

3
ระวังเนื่องจากการแปลง int64_t เป็นสองเท่าอาจทำให้สูญเสียดังนั้นจึงไม่เป็นประเภททั่วไปเนื่องจากอาจปรากฏขึ้น
Adrian McCarthy

@AdrianMcCarthy ใช่คุณต้องสร้างความเชี่ยวชาญเทมเพลตที่ถูกต้องตามที่แสดงด้านบน ที่คุณสามารถดูผมใช้สองฟังก์ชั่นเพิ่มเติมสำหรับการและlong long long doubleต้องทำเช่นเดียวกันสำหรับอีกสองฟังก์ชั่น
Flovdis

ฉันคิดว่านี่เป็นสิ่งที่ช้าที่สุด แต่ก็ไม่จำเป็นที่จะต้องเป็น สิ่งที่คุณต้องทำคือการ std :: enable_if_t และทำสองสาขาสำหรับจำนวนเต็มและลอย คุณสามารถใช้ numeric_limits ได้ดีขึ้นและดูว่า mantissa มีขนาดใหญ่พอที่จะปรับให้เข้ากับค่าจริงหรือไม่ ที่จะเพิ่มความปลอดภัย
สุกร

5

ก่อนอื่นเงื่อนไขข้อผิดพลาดของคุณ (หลาย == 0) น่าจะมีค่าตอบแทน อะไร? ฉันไม่รู้ บางทีคุณต้องการที่จะโยนข้อยกเว้นนั่นก็ขึ้นอยู่กับคุณ แต่การคืนกลับไม่มีสิ่งใดเป็นอันตราย

ประการที่สองคุณควรตรวจสอบว่า numToRound ยังไม่ได้มีหลายรายการ มิฉะนั้นเมื่อคุณเพิ่มmultipleไปroundDownคุณจะได้คำตอบที่ไม่ถูกต้อง

ประการที่สามการปลดเปลื้องของคุณผิด คุณหล่อnumToRoundไปยังจำนวนเต็ม แต่เป็นจำนวนเต็มอยู่แล้ว คุณต้องร่ายเป็นสองเท่าก่อนหารและกลับสู่ 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);
}

@ ปีเตอร์มันคืออะไร? ฉันคิดว่าint / intมันจะคืนค่า int ซึ่งไม่ใช่สิ่งที่เราต้องการ
Mike Caron

int / int จะคืนค่า int จริง ๆ แต่นั่นคือสิ่งที่คุณต้องการ ตัวอย่างเช่น numToRound = 7, multiple = 3. 7/3 = 2
Peter Ruderman

4

รอบสู่พลังของสอง:

ในกรณีที่ทุกคนต้องการทางออกสำหรับตัวเลขบวกจะปัดเศษเป็นทวีคูณของกำลังสองที่ใกล้เคียงที่สุด (เพราะนั่นคือวิธีที่ฉันสิ้นสุดที่นี่):

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

หมายเลขอินพุทจะยังคงเหมือนเดิมหากมีหลายค่าอยู่แล้ว

นี่คือเอาต์พุต x86_64 ที่ GCC ให้ด้วย-O2หรือ-Os(9Sep2013 Build - godbolt GCC ออนไลน์):

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ฟังก์ชันเมื่อใช้งาน


เครดิต:


1
สิ่งที่ฉันกำลังมองหา ขอบคุณ!
kiyo

1
int roundUpPow2 (int NUM, int pow2) {return NUM + (pow2 - 1) & ~ (pow2 - 1); } เร็วกว่าประมาณ 30% และใช้งานง่ายกว่า (คุณผ่าน 16 ไม่ใช่ 4 เพื่อปัดเศษเป็นทวีคูณถัดไปของ 16
Axel Rietschin

3

ฉันกำลังใช้:

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

ฉันใช้ของคุณn_Align_Up_POTนับตั้งแต่ที่ฉันเห็นมันในชั้นเรียน TList ของ Delphi มันมีข้อ จำกัด ของมันเช่นการจัดตำแหน่ง (หลายครั้ง) ที่เป็นกำลัง 2 แต่นั่นไม่ค่อยมีปัญหาเพราะส่วนใหญ่ฉันใช้เพื่อรับ / ตรวจสอบการจัดตำแหน่งที่ถูกต้องสำหรับ SMID มันยอดเยี่ยมและดูเหมือนว่าไม่มีใครรู้เกี่ยวกับมัน
user1593842

2

อาจปลอดภัยกว่าที่จะร่ายเพื่อลอยและใช้ ceil () - เว้นแต่คุณจะรู้ว่าการแบ่ง int จะให้ผลลัพธ์ที่ถูกต้อง


1
โปรดทราบว่าสองเท่าสามารถเก็บซิกนิฟิแคนด์ 54 บิตบนเครื่องที่ใช้ x86 หากคุณมี int 64- บิตมันจะล้มเหลวในที่สุด
สุกร

มาตรฐาน IEEE754 สองเท่าไม่สามารถทำได้ แต่ x64 cpus มีจุดลอยตัวภายใน 80 บิตดังนั้นการดำเนินการกับหมายเลขเดียวจึงน่าเชื่อถือ
Martin Beckett

1
ในขณะที่เป็นจริงคุณสามารถควบคุมการปัดเศษจาก C / C ++ ได้น้อยมาก ขึ้นอยู่กับการตั้งค่าคำควบคุมและจริง ๆ แล้วอาจปัดเป็นน้อยกว่า 80 บิต นอกจากนี้คุณยังมี SSE และชุดคำสั่ง SIMD อื่น ๆ ที่ไม่มีสื่อขยายเพิ่มเติม (vectorizing คอมไพเลอร์สามารถใช้งานได้ง่าย)
สุกร

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

C ++ ปัดเศษตัวเลขแต่ละตัวลงดังนั้นถ้าคุณเพิ่ม 0.5 (ถ้าเป็น 1.5 มันจะเท่ากับ 2) แต่ 1.49 จะเท่ากับ 1.99 ดังนั้น 1

แก้ไข - ขออภัยไม่เห็นว่าคุณต้องการปัดเศษฉันขอแนะนำให้ใช้วิธี ceil () แทนที่จะเป็น +0.5


2

ดีสำหรับสิ่งหนึ่งเนื่องจากฉันไม่เข้าใจสิ่งที่คุณต้องการทำเส้น

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

สามารถย่อให้สั้นลงได้

int roundUp = roundDown + multiple;
return roundUp;

2

อาจเป็นสิ่งนี้สามารถช่วย:

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

เหตุใดจึงใช้การแบ่งชั้นและจำนวนเต็ม ไม่มีอะไรถึงพื้น หากเป็นสองเท่าอย่างน้อยคุณสามารถสืบทอดการจัดการค่าลบได้
สุกร

2

ให้ปัดขึ้นเสมอ

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

ปัดเศษเป็นทวีคูณที่ใกล้เคียงที่สุดซึ่งมีค่าเป็น 2

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

สิ่งนี้มีประโยชน์สำหรับเมื่อจัดสรรตาม cachelines โดยที่การเพิ่มการปัดเศษที่คุณต้องการคือพลังของสอง แต่ค่าผลลัพธ์จะต้องเป็นผลคูณของมันเท่านั้น ในgccร่างกายของฟังก์ชั่นนี้สร้าง 8 คำแนะนำการชุมนุมโดยไม่มีการแบ่งหรือสาขา

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

1

ฉันพบอัลกอริทึมที่ค่อนข้างคล้ายกับโพสต์ด้านบน:

int [(| | | n-1) / n] * [(nx) / | x |], โดยที่ x คือค่าที่ผู้ใช้ป้อนและ n คือค่าที่ใช้

มันทำงานได้สำหรับค่าทั้งหมด x โดยที่ x เป็นจำนวนเต็ม (บวกหรือลบรวมถึงศูนย์) ฉันเขียนมันโดยเฉพาะสำหรับโปรแกรม C ++ แต่โดยทั่วไปสามารถนำไปใช้ในภาษาใดก็ได้


1

สำหรับค่าลบ 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);
}

1

นี่คือสิ่งที่ฉันจะทำ:

#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ไปยังfloatสูญเสียความแม่นยำและทำให้คำตอบไม่ถูกต้อง
chux - Reinstate Monica

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

แม้ว่า:

  • จะไม่ทำงานกับจำนวนลบ
  • จะไม่ทำงานหาก numRound + หลายโอเวอร์โฟลว์

จะแนะนำให้ใช้จำนวนเต็มไม่ได้ลงนามแทนซึ่งได้กำหนดพฤติกรรมล้น

คุณจะได้รับการยกเว้นหลาย == 0 แต่มันไม่ใช่ปัญหาที่ชัดเจนในกรณีนั้น


1

ค:

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

ฉันใช้การรวมกันของโมดูลัสเพื่อลบล้างการเพิ่มส่วนที่เหลือถ้าxเป็นหลายแล้ว:

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

xเราพบผกผันของส่วนที่เหลือแล้วโมดูลัสที่มีตัวหารอีกครั้งเพื่อลบล้างมันถ้ามันเป็นตัวหารของตัวเองแล้วเพิ่ม

round_up(19, 3) = 21

1

นี่คือทางออกของฉันตามคำแนะนำของ 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;
}

ล้มเหลวด้วยRoundUp(INT_MIN, -1)ตามที่n / multipleเป็นintล้น
chux - Reinstate Monica

1

ฉันคิดว่านี่จะช่วยคุณได้ ฉันได้เขียนโปรแกรมด้านล่างเป็น 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

นี่คือผลลัพธ์ที่คุณต้องการสำหรับจำนวนเต็มบวก:

#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

ฉันคิดว่ามันใช้งานได้:

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

-1

สิ่งนี้ได้ผลสำหรับฉัน แต่ไม่ได้พยายามจัดการกับเชิงลบ

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

นี่คือทางออกที่ง่ายที่สุดในการแสดงแนวคิดของความสง่างาม มันเป็นพื้นยึดตาราง

(รหัสหลอก)

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

คุณตรวจสอบความคิดของคุณก่อนส่งหรือไม่ มันเป็นปริมาณมากให้คำตอบที่ถูกต้อง
yaodav

นั่นไม่ใช่แม้แต่รหัสที่ถูกต้อง
user13783520
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.