ฉันจะมั่นใจได้อย่างไรว่าการหารจำนวนเต็มจะปัดเศษขึ้นเสมอ?


242

ฉันต้องการให้แน่ใจว่าการหารจำนวนเต็มจะถูกปัดเศษขึ้นเสมอหากจำเป็น มีวิธีที่ดีกว่านี้ไหม มีการหล่อที่เกิดขึ้นมากมาย :-)

(int)Math.Ceiling((double)myInt1 / myInt2)

48
คุณสามารถกำหนดสิ่งที่คุณพิจารณาว่า "ดีกว่า" ได้ชัดเจนขึ้นหรือไม่? ได้เร็วขึ้น? ลงเรื่อย ๆ ? แม่นยำกว่านี้ไหม แข็งแกร่งกว่านี้ไหม? เห็นได้ชัดว่าถูกต้องมากขึ้น?
Eric Lippert

6
คุณมักจะมีการคัดเลือกนักคณิตศาสตร์ใน C # เสมอ - นั่นเป็นเหตุผลว่าทำไมมันจึงไม่ใช่ภาษาที่ยอดเยี่ยมสำหรับสิ่งนี้ คุณต้องการค่าปัดเศษขึ้นหรืออยู่ห่างจากศูนย์ - ควร -3.1 ไป -3 (ขึ้นไป) หรือ -4 (ห่างจากศูนย์)
คี ธ

9
Eric: คุณหมายถึงอะไร "แม่นยำยิ่งขึ้นแข็งแกร่งกว่าหรือไม่ชัดเจนกว่านี้" จริงๆแล้วสิ่งที่ฉันทำหมายถึงแค่ "ดีกว่า" ฉันจะให้ผู้อ่านใส่ความหมายลงไปให้ดีขึ้น ดังนั้นหากใครบางคนมีโค้ดที่สั้นกว่านี้เยี่ยมมากถ้าอีกคนมีโค้ดที่เร็วกว่าก็ดี :-) คุณมีคำแนะนำอะไรเกี่ยวกับคุณบ้าง?
Karsten

1
ฉันเป็นคนเดียวที่อ่านชื่อแม้ว่า "โอ้มันเป็นบทสรุปของ C #?"
Matt Ball

6
เป็นเรื่องที่น่าอัศจรรย์จริง ๆ ว่าคำถามนี้เป็นคำถามที่ยากเพียงใดและการอภิปรายมีประโยชน์อย่างไร
Justin Morgan

คำตอบ:


669

UPDATE: คำถามนี้เป็นเรื่องของบล็อกของฉันในเดือนมกราคม 2013 ขอบคุณสำหรับคำถามที่ยอดเยี่ยม!


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

วิธีที่ถูกต้องในการเข้าหาปัญหาเลขคณิตจำนวนเต็มนั่นคือวิธีที่เพิ่มโอกาสในการได้คำตอบที่ถูกต้องตั้งแต่ครั้งแรกคือการเข้าหาปัญหาอย่างรอบคอบแก้ปัญหาทีละขั้นตอนและใช้หลักการทางวิศวกรรมที่ดีในการทำ ดังนั้น.

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

  1. การหารจะปัดผลลัพธ์เป็นศูนย์

  2. ผลลัพธ์เป็นศูนย์หรือบวกเมื่อตัวถูกดำเนินการทั้งสองมีเครื่องหมายเหมือนกันและศูนย์หรือลบเมื่อตัวถูกดำเนินการทั้งสองมีเครื่องหมายตรงข้าม

  3. หากตัวถูกดำเนินการด้านซ้ายเป็น int ที่สามารถแทนค่าได้น้อยที่สุดและตัวถูกดำเนินการด้านขวาคือ –1 จะเกิดการล้น [... ] มันคือการดำเนินการที่กำหนดว่าเป็น [ArithmeticException] ถูกโยนหรือล้นไปไม่ได้รายงานกับค่าผลลัพธ์เป็นที่ของตัวถูกดำเนินการด้านซ้าย

  4. ถ้าค่าของตัวถูกดำเนินการด้านขวาเป็นศูนย์ System.DivideByZeroException จะถูกส่งออกไป

สิ่งที่เราต้องการคือฟังก์ชั่นแบ่งจำนวนเต็มซึ่งคำนวณฉลาด แต่รอบผลเสมอขึ้นไม่เสมอไปทางศูนย์

ดังนั้นเขียนสเปคสำหรับฟังก์ชันนั้น ฟังก์ชั่นของเราint DivRoundUp(int dividend, int divisor)จะต้องมีพฤติกรรมที่กำหนดไว้สำหรับทุกการป้อนข้อมูลที่เป็นไปได้ พฤติกรรมที่ไม่ได้กำหนดนั้นเป็นสิ่งที่น่ากังวลอย่างมากดังนั้นให้กำจัดมันออกไป เราจะบอกว่าการดำเนินการของเรามีข้อกำหนดนี้:

  1. การดำเนินการพ่นถ้าตัวหารเป็นศูนย์

  2. การดำเนินการโยนถ้าเงินปันผลเป็น int.minval และตัวหารคือ -1

  3. หากไม่มีส่วนที่เหลือ - การหารคือ 'คู่' - ดังนั้นค่าส่งคืนคือความฉลาดทางอินทิเกรต

  4. มิฉะนั้นก็จะส่งกลับมีขนาดเล็กที่สุดจำนวนเต็มที่มีมากขึ้นกว่าความฉลาดที่เป็นก็มักจะปัดเศษขึ้น

ตอนนี้เรามีสเปคเพื่อให้เรารู้ว่าเราสามารถเกิดขึ้นกับการออกแบบทดสอบ สมมติว่าเราเพิ่มเกณฑ์การออกแบบเพิ่มเติมว่าปัญหาจะได้รับการแก้ไขด้วยเลขคณิตเลขจำนวนเต็มแทนที่จะคำนวณความฉลาดเป็นสองเท่าเนื่องจากการแก้ปัญหา "double" ถูกปฏิเสธอย่างชัดเจนในคำชี้แจงปัญหา

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

ตอนนี้เรามีสเปคและการออกแบบเราสามารถเริ่มเขียนโค้ดได้

public static int DivRoundUp(int dividend, int divisor)
{
  if (divisor == 0 ) throw ...
  if (divisor == -1 && dividend == Int32.MinValue) throw ...
  int roundedTowardsZeroQuotient = dividend / divisor;
  bool dividedEvenly = (dividend % divisor) == 0;
  if (dividedEvenly) 
    return roundedTowardsZeroQuotient;

  // At this point we know that divisor was not zero 
  // (because we would have thrown) and we know that 
  // dividend was not zero (because there would have been no remainder)
  // Therefore both are non-zero.  Either they are of the same sign, 
  // or opposite signs. If they're of opposite sign then we rounded 
  // UP towards zero so we're done. If they're of the same sign then 
  // we rounded DOWN towards zero, so we need to add one.

  bool wasRoundedDown = ((divisor > 0) == (dividend > 0));
  if (wasRoundedDown) 
    return roundedTowardsZeroQuotient + 1;
  else
    return roundedTowardsZeroQuotient;
}

มันฉลาดไหม ไม่สวยหรอ ไม่สั้น ไม่ถูกต้องตามข้อกำหนด? ฉันเชื่ออย่างนั้น แต่ฉันยังไม่ได้ทดสอบอย่างเต็มที่ มันดูดีทีเดียว

เราเป็นมืออาชีพที่นี่; ใช้วิธีปฏิบัติทางวิศวกรรมที่ดี วิจัยเครื่องมือของคุณระบุพฤติกรรมที่ต้องการพิจารณากรณีข้อผิดพลาดก่อนและเขียนรหัสเพื่อเน้นความถูกต้องชัดเจน และเมื่อคุณพบข้อผิดพลาดให้พิจารณาว่าอัลกอริทึมของคุณมีข้อบกพร่องอย่างลึกล้ำที่จะเริ่มต้นด้วยก่อนที่คุณจะเริ่มสุ่มสลับทิศทางของการเปรียบเทียบรอบ ๆ และแบ่งสิ่งที่ใช้งานได้แล้ว


44
คำตอบที่เป็นแบบอย่างที่ดีเยี่ยม
Gavin Miller

61
สิ่งที่ฉันสนใจไม่ใช่พฤติกรรม พฤติกรรมอย่างใดอย่างหนึ่งดูเหมือนว่าสมเหตุสมผล สิ่งที่ฉันสนใจคือไม่ได้ระบุไว้ซึ่งหมายความว่าไม่สามารถทดสอบได้อย่างง่ายดาย ในกรณีนี้เรากำลังกำหนดผู้ให้บริการของเราเองเพื่อให้เราสามารถระบุพฤติกรรมที่เราชอบ ฉันไม่สนใจว่าพฤติกรรมนั้นจะ "โยน" หรือ "ไม่ทิ้ง" แต่ฉันก็สนใจว่ามันจะถูกระบุไว้
Eric Lippert

68
ยี้มันคุยโวโอ้อวดล้มเหลว :(
จอน Skeet

32
ผู้ชาย - คุณช่วยเขียนหนังสือเกี่ยวกับเรื่องนั้นได้ไหม
xtofl

76
@finnw: ไม่ว่าฉันจะทดสอบหรือไม่ไม่เกี่ยวข้อง การแก้ปัญหาเลขจำนวนเต็มนี้ไม่ใช่ปัญหาทางธุรกิจของฉัน ถ้าเป็นแล้วฉันจะทดสอบมัน หากมีคนต้องการใช้รหัสจากคนแปลกหน้านอกอินเทอร์เน็ตเพื่อแก้ปัญหาทางธุรกิจของพวกเขาแล้วความรับผิดชอบอยู่ที่พวกเขาเพื่อทดสอบอย่างละเอียด
Eric Lippert

49

คำตอบทั้งหมดที่นี่ดูเหมือนจะซับซ้อนเกินไป

ใน C # และ Java สำหรับการปันผลและตัวหารที่เป็นบวกคุณต้องทำ:

( dividend + divisor - 1 ) / divisor 

ที่มา: การแปลงตัวเลข, Roland Backhouse, 2001


น่ากลัว ถึงแม้ว่าคุณควรเพิ่มวงเล็บเพื่อลบความกำกวมการพิสูจน์ว่ามันยาวไปหน่อย แต่คุณสามารถรู้สึกได้ในอุทรว่ามันถูกต้องเพียงแค่มองมัน
Jörgen Sigvardsson

1
อืม ... แล้วเงินปันผล = 4 ตัวหาร = (- 2) ??? 4 / (-2) = (-2) = (-2) หลังจากถูกปัดขึ้น แต่อัลกอริทึมที่คุณให้ไว้ (4 + (-2) - 1) / (-2) = 1 / (-2) = (-0.5) = 0 หลังจากที่ถูกปัดเศษขึ้น
สกอตต์

1
@Scott - ขอโทษที่ฉันไม่ได้พูดถึงว่าวิธีการแก้ปัญหานี้ถือเป็นเพียงเงินปันผลและตัวหารที่เป็นบวก ฉันได้อัปเดตคำตอบของฉันเพื่อพูดถึงการชี้แจงนี้
Ian Nelson

1
ผมชอบมันแน่นอนคุณอาจจะมีล้นค่อนข้างเทียมเศษเป็นผลพลอยได้ของวิธีการนี้ ...
ทีซีซี

2
@PIntag: ความคิดเป็นสิ่งที่ดี แต่การใช้โมดูโล่ผิด รับ 13 และ 3 ผลลัพธ์ที่คาดหวัง 5 แต่((13-1)%3)+1)ให้ผลลัพธ์ 1 รายการ การแบ่งประเภทที่เหมาะสม1+(dividend - 1)/divisorให้ผลลัพธ์เช่นเดียวกับคำตอบของเงินปันผลและตัวหารที่เป็นบวก นอกจากนี้ยังไม่มีปัญหาล้น แต่เทียมพวกเขาอาจจะ
Lutz Lehmann

48

คำตอบพื้นฐานตามสุดท้าย

สำหรับจำนวนเต็มที่ลงนาม:

int div = a / b;
if (((a ^ b) >= 0) && (a % b != 0))
    div++;

สำหรับจำนวนเต็มที่ไม่ได้ลงนาม:

int div = a / b;
if (a % b != 0)
    div++;

เหตุผลสำหรับคำตอบนี้

การแบ่งจำนวนเต็ม ' /' ถูกกำหนดให้ปัดไปทางศูนย์ (7.7.2 ของข้อมูลจำเพาะ) แต่เราต้องการปัดเศษขึ้น ซึ่งหมายความว่าคำตอบเชิงลบจะถูกปัดเศษอย่างถูกต้องแล้ว แต่จำเป็นต้องปรับคำตอบเชิงบวก

คำตอบเชิงบวกที่ไม่เป็นศูนย์นั้นง่ายต่อการตรวจสอบ แต่คำตอบที่เป็นศูนย์นั้นมีเล่ห์เหลี่ยมเล็กน้อยเนื่องจากนั่นอาจเป็นการปัดเศษของค่าลบหรือการปัดเศษของค่าบวก

เดิมพันที่ปลอดภัยที่สุดคือการตรวจสอบเมื่อคำตอบควรเป็นบวกโดยตรวจสอบว่าสัญญาณของจำนวนเต็มทั้งสองเหมือนกัน ตัวดำเนินการ xor จำนวนเต็ม ' ^' บนค่าสองค่าจะทำให้ 0 sign-bit เมื่อเป็นกรณีนี้หมายถึงผลลัพธ์ที่ไม่เป็นลบดังนั้นการตรวจสอบ(a ^ b) >= 0จะพิจารณาว่าผลลัพธ์ควรเป็นค่าบวกก่อนที่จะปัดเศษ นอกจากนี้โปรดทราบว่าสำหรับจำนวนเต็มที่ไม่ได้ลงชื่อทุกคำตอบเป็นบวกอย่างเห็นได้ชัดดังนั้นการตรวจสอบนี้สามารถละเว้น

การตรวจสอบที่เหลืออยู่เพียงอย่างเดียวคือว่ามีการปัดเศษเกิดขึ้นหรือไม่ซึ่งa % b != 0จะทำงาน

บทเรียนที่ได้เรียนรู้

เลขคณิต (จำนวนเต็มหรืออย่างอื่น) นั้นไม่ง่ายอย่างที่คิด คิดอย่างถี่ถ้วนทุกครั้ง

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

เพื่อให้ได้ความรู้สึกที่แน่นอนเกี่ยวกับคำตอบของจุดลอยผมต้องทำเพิ่มเติม (และอาจจะซับซ้อนกว่า) โดยคิดว่ามีเงื่อนไขใด ๆ ที่ความแม่นยำของจุดลอยตัวอาจเข้าทางและMath.Ceilingอาจเป็นไปได้หรือไม่ สิ่งที่ไม่พึงประสงค์ในอินพุต 'เหมาะสม'

เส้นทางเดินทาง

แทนที่ (โปรดทราบฉันเปลี่ยนวินาทีmyInt1ด้วยmyInt2สมมติว่าเป็นสิ่งที่คุณหมายถึง):

(int)Math.Ceiling((double)myInt1 / myInt2)

ด้วย:

(myInt1 - 1 + myInt2) / myInt2

ข้อแม้เพียงอย่างเดียวว่าถ้าmyInt1 - 1 + myInt2ล้นชนิดจำนวนเต็มที่คุณกำลังใช้คุณอาจจะไม่ได้รับสิ่งที่คุณคาดหวัง

เหตุผลนี้ผิด : -1000000 และ 3999 ควรให้ -250 นี่ให้ -249

แก้ไข:
พิจารณานี้มีข้อผิดพลาดเช่นเดียวกับการแก้ปัญหาอื่น ๆ สำหรับจำนวนเต็มเชิงลบmyInt1ค่ามันอาจจะง่ายที่จะทำสิ่งที่ชอบ:

int rem;
int div = Math.DivRem(myInt1, myInt2, out rem);
if (rem > 0)
  div++;

ที่ควรให้ผลลัพธ์ที่ถูกต้องในการdivใช้การดำเนินการจำนวนเต็มเท่านั้น

เหตุผลนี้ผิด : -1 และ -5 ควรให้ 1, นี่ให้ 0

แก้ไข (อีกครั้งด้วยความรู้สึก):
ผู้ดำเนินการแผนกปัดเศษเป็นศูนย์ สำหรับผลลัพธ์เชิงลบสิ่งนี้ถูกต้องดังนั้นเฉพาะผลลัพธ์ที่ไม่ใช่เชิงลบเท่านั้นที่ต้องมีการปรับ นอกจากนี้ยังพิจารณาว่าDivRemเพียงแค่ทำ/และ%ต่อไปลองข้ามสาย (และเริ่มต้นด้วยการเปรียบเทียบง่าย ๆ เพื่อหลีกเลี่ยงการคำนวณแบบโมดูโลเมื่อไม่จำเป็น):

int div = myInt1 / myInt2;
if ((div >= 0) && (myInt1 % myInt2 != 0))
    div++;

เหตุผลนี้ผิด : -1 และ 5 ควรให้ 0 นี่ให้ 1

(ในการป้องกันของตัวเองของความพยายามครั้งสุดท้ายของฉันฉันไม่ควรพยายามตอบเหตุผลในขณะที่ใจของฉันบอกฉันว่าฉันนอนดึก 2 ชั่วโมง)


19

โอกาสที่สมบูรณ์แบบในการใช้ส่วนขยาย:

public static class Int32Methods
{
    public static int DivideByAndRoundUp(this int number, int divideBy)
    {                        
        return (int)Math.Ceiling((float)number / (float)divideBy);
    }
}

ทำให้โค้ดของคุณสามารถอ่านได้ด้วย:

int result = myInt.DivideByAndRoundUp(4);

1
เอ่ออะไรนะ? รหัสของคุณจะถูกเรียกว่า myInt.DivideByAndRoundUp () และจะคืนค่า 1 เสมอสำหรับอินพุต 0 ซึ่งจะทำให้เกิดข้อยกเว้น ...
กำหนดค่า

5
ความล้มเหลวของมหากาพย์ (-2) .DivideByAndRoundUp (2) ผลตอบแทน 0
Timwi

3
ฉันไปงานปาร์ตี้สายจริงๆ แต่รหัสนี้รวบรวมหรือไม่ คลาส Math ของฉันไม่มีเมธอด Ceiling ที่รับสองอาร์กิวเมนต์
R. Martinho Fernandes

17

คุณสามารถเขียนผู้ช่วย

static int DivideRoundUp(int p1, int p2) {
  return (int)Math.Ceiling((double)p1 / p2);
}

1
ยังคงมีจำนวนการคัดเลือกนักแสดงที่เท่ากัน
ChrisF

29
@Outlaw เข้าใจทุกสิ่งที่คุณต้องการ แต่สำหรับฉันหากพวกเขาไม่ใส่คำถามฉันมักจะคิดว่าพวกเขาไม่ได้พิจารณา
JaredPar

1
การเขียนผู้ช่วยนั้นไร้ประโยชน์หากเป็นเช่นนั้น ให้เขียนผู้ช่วยด้วยชุดการทดสอบที่ครอบคลุมแทน
dolmen

3
@dolmen คุณคุ้นเคยกับแนวคิดของการใช้รหัส ซ้ำหรือไม่ oO
Rushyo

4

คุณสามารถใช้สิ่งต่อไปนี้

a / b + ((Math.Sign(a) * Math.Sign(b) > 0) && (a % b != 0)) ? 1 : 0)

12
เห็นได้ชัดว่ารหัสนี้ผิดในสองวิธี ก่อนอื่นมีข้อผิดพลาดเล็กน้อยในไวยากรณ์ คุณต้องการวงเล็บเพิ่ม แต่ที่สำคัญกว่านั้นไม่ได้คำนวณผลลัพธ์ที่ต้องการ ตัวอย่างเช่นลองทดสอบด้วย = -1000000 และ b = 3999 ผลลัพธ์การหารจำนวนเต็มปกติคือ -250 การหารสองครั้งคือ -250.0625 ... พฤติกรรมที่ต้องการคือการปัดเศษ เห็นได้ชัดว่าการปัดเศษที่ถูกต้องขึ้นจาก -250.0625 คือการปัดเศษขึ้นเป็น -250 แต่รหัสของคุณปัดขึ้นเป็น -249
Eric Lippert

36
ฉันขอโทษที่ต้องพูดอย่างนี้ต่อไป แต่รหัสของคุณยังไม่ถูกต้องแดเนียล 1/2 ควรปัดเศษขึ้นเป็น 1 แต่รหัสของคุณจะปัดลงเป็น 0 ทุกครั้งที่พบข้อผิดพลาดที่คุณ "แก้ไข" โดยแนะนำบั๊กอื่น คำแนะนำของฉัน: หยุดทำอย่างนั้น เมื่อมีคนพบข้อผิดพลาดในรหัสของคุณอย่าเพิ่งตบการแก้ไขด้วยกันโดยไม่คำนึงถึงสิ่งที่ทำให้เกิดข้อผิดพลาดในตอนแรก ใช้วิธีปฏิบัติทางวิศวกรรมที่ดี ค้นหาข้อบกพร่องในอัลกอริทึมและแก้ไข ข้อบกพร่องของอัลกอริทึมรุ่นที่ไม่ถูกต้องทั้งสามรุ่นของคุณคือคุณไม่ได้กำหนดอย่างถูกต้องเมื่อการปัดเศษเป็น "ลง"
Eric Lippert

8
ไม่น่าเชื่อว่ามีข้อบกพร่องมากมายในโค้ดชิ้นเล็กชิ้นนี้ ฉันไม่เคยมีเวลามากที่จะคิดเกี่ยวกับมัน - ผลปรากฏในความคิดเห็น (1) a * b> 0 จะถูกต้องถ้ามันไม่ได้ล้น มีการรวมกัน 9 รายการสำหรับสัญลักษณ์ของ a และ b - [-1, 0, +1] x [-1, 0, +1] เราสามารถข้ามกรณี b == 0 เหลือ 6 กรณี [-1, 0, +1] x [-1, +1] a / b ปัดเศษเป็นศูนย์นั่นคือการปัดเศษขึ้นสำหรับผลลัพธ์เชิงลบและปัดเศษลงสำหรับ resit positve ดังนั้นการปรับจะต้องดำเนินการถ้า a และ b มีเครื่องหมายเหมือนกันและไม่เป็นศูนย์ทั้งคู่
Daniel Brückner

5
คำตอบนี้อาจเป็นสิ่งที่แย่ที่สุดที่ฉันเขียนไว้ใน SO ... และตอนนี้มันเชื่อมโยงกับบล็อกของ Eric ... ดีความตั้งใจของฉันคือไม่ได้ให้วิธีแก้ปัญหาที่อ่านได้ ฉันล็อคแฮ็คสั้นและเร็วจริงๆ และเพื่อปกป้องทางออกของฉันอีกครั้งฉันได้ความคิดที่ถูกต้องในครั้งแรก แต่ไม่ได้คิดเกี่ยวกับการล้น เห็นได้ชัดว่าเป็นความผิดพลาดของฉันในการโพสต์รหัสโดยไม่ต้องเขียนและทดสอบใน VisualStudio "การแก้ไข" ยิ่งแย่ลง - ฉันไม่ได้ตระหนักว่ามันเป็นปัญหาที่ล้นและคิดว่าฉันทำผิดเชิงตรรกะ ผลที่ตามมา "แก้ไข" ครั้งแรกก็ไม่ได้เปลี่ยนแปลงอะไรเลย ฉันเพิ่งกลับ
Daniel Brückner

10
ตรรกะและผลักดันข้อผิดพลาดรอบ ๆ ที่นี่ฉันทำผิดพลาดต่อไป; อย่างที่ Eric พูดถึงแล้วฉันไม่ได้วิเคราะห์บั๊กจริงๆและเพิ่งทำสิ่งแรกที่ดูเหมือนถูก และฉันยังไม่ได้ใช้ VisualStudio โอเคฉันรีบและไม่ใช้เวลาเกินห้านาทีในการ "แก้ไข" แต่นี่ไม่ควรเป็นข้อแก้ตัว หลังจากที่ฉันเอริคชี้ให้เห็นข้อผิดพลาดซ้ำแล้วซ้ำเล่าฉันเริ่มใช้ VisualStudio และพบปัญหาจริง การแก้ไขโดยใช้เครื่องหมาย () ทำให้สิ่งที่อ่านไม่ได้มากขึ้นและเปลี่ยนเป็นรหัสที่คุณไม่ต้องการดูแลรักษา ผมได้เรียนรู้บทเรียนของฉันและจะไม่ประมาทวิธีการที่ยุ่งยากอีกต่อไป
แดเนียลBrückner

-2

คำตอบข้างต้นบางข้อใช้ลอยตัวซึ่งไม่มีประสิทธิภาพและไม่จำเป็นจริงๆ สำหรับ ints ที่ไม่ได้ลงชื่อนี่คือคำตอบที่มีประสิทธิภาพสำหรับ int1 / int2:

(int1 == 0) ? 0 : (int1 - 1) / int2 + 1;

สำหรับ ints ที่ลงนามแล้วจะไม่ถูกต้อง


ไม่ใช่สิ่งที่ OP ถามในตอนแรกไม่ได้เพิ่มคำตอบอื่น ๆ
santamanno

-4

ปัญหาของการแก้ปัญหาทั้งหมดที่นี่คือพวกเขาต้องการนักแสดงหรือพวกเขามีปัญหาเชิงตัวเลข การหล่อเพื่อลอยหรือเป็นสองเท่าเป็นทางเลือกเสมอ แต่เราทำได้ดีกว่า

เมื่อคุณใช้รหัสของคำตอบจาก @jerryjvl

int div = myInt1 / myInt2;
if ((div >= 0) && (myInt1 % myInt2 != 0))
    div++;

มีข้อผิดพลาดในการปัดเศษ 1/5 จะปัดเศษขึ้นเนื่องจาก 1% 5! = 0 แต่นี่เป็นความผิดเพราะการปัดเศษจะเกิดขึ้นก็ต่อเมื่อคุณแทนที่ 1 ด้วย 3 ดังนั้นผลลัพธ์คือ 0.6 เราต้องหาวิธีที่จะปัดเศษขึ้นเมื่อการคำนวณให้ค่าที่มากกว่าหรือเท่ากับ 0.5 ผลลัพธ์ของโอเปอเรเตอร์โมดูโลในตัวอย่างด้านบนมีช่วงตั้งแต่ 0 ถึง myInt2-1 การปัดเศษจะเกิดขึ้นก็ต่อเมื่อส่วนที่เหลือมากกว่า 50% ของตัวหาร ดังนั้นรหัสที่ปรับจะมีลักษณะดังนี้:

int div = myInt1 / myInt2;
if (myInt1 % myInt2 >= myInt2 / 2)
    div++;

แน่นอนว่าเรามีปัญหาการปัดเศษที่ myInt2 / 2 เช่นกัน แต่ผลลัพธ์นี้จะช่วยให้คุณมีวิธีการปัดเศษที่ดีกว่าอีกวิธีหนึ่งในเว็บไซต์นี้


"เราต้องหาวิธีที่จะปัดเศษขึ้นเมื่อการคำนวณให้ค่ามากกว่าหรือเท่ากับ 0.5" - คุณพลาดจุดของคำถามนี้ - หรือปัดเศษขึ้นเสมอเช่น OP ต้องการปัดเศษ 0.001 ถึง 1
Grhm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.