Java ปัดเศษขึ้นเป็น int โดยใช้ Math.ceil


102
int total = (int) Math.ceil(157/32);

ทำไมมันยังกลับ 4? 157/32 = 4.90625, ฉันต้องสรุป, ฉันมองไปรอบ ๆ และดูเหมือนจะเป็นวิธีการที่ถูกต้อง

ฉันพยายามtotalเป็นdoubleประเภท แต่ได้รับ 4.0

ผมทำอะไรผิดหรือเปล่า?

คำตอบ:


192

คุณกำลังทำ157/32ซึ่งกำลังหารจำนวนเต็มสองจำนวนด้วยกันซึ่งจะส่งผลให้จำนวนเต็มปัดลงเสมอ ดังนั้นจึง(int) Math.ceil(...)ไม่ได้ทำอะไร มีสามวิธีที่เป็นไปได้เพื่อให้บรรลุสิ่งที่คุณต้องการ ผมขอแนะนำให้ใช้ตัวเลือก 1หรือ2 ตัวเลือก กรุณาอย่าไม่ใช้ตัวเลือก 0

## ตัวเลือก 0

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

int n = (int) Math.ceil((double) a / b));

##ตัวเลือกที่ 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

คุณทำa / bพื้นเสมอถ้าaและbเป็นจำนวนเต็มทั้งคู่ จากนั้นคุณจะมี if-statement Witch แบบอินไลน์ตรวจสอบว่าคุณควรหยุดแทนพื้นหรือไม่ ดังนั้น +1 หรือ +0 หากมีส่วนที่เหลืออยู่ในส่วนที่คุณต้องการ +1 a % b == 0ตรวจสอบส่วนที่เหลือ

## ทางเลือกที่ 2

ตัวเลือกนี้สั้นมาก แต่อาจใช้งานง่ายสำหรับบางคน ผมคิดว่าวิธีการที่ใช้งานง่ายน้อยนี้จะเร็วกว่าคู่ส่วนและการเปรียบเทียบวิธีการ: หมายเหตุโปรดที่ว่านี้ไม่ได้ทำงาน
b < 0

int n = (a + b - 1) / b;

เพื่อลดโอกาสการล้นคุณสามารถใช้สิ่งต่อไปนี้ อย่างไรก็ตามโปรดทราบว่ามันไม่ได้ทำงานให้และa = 0b < 1

int n = (a - 1) / b + 1;

## คำอธิบายเบื้องหลัง "แนวทางที่ใช้งานง่ายน้อย"

เนื่องจากการหารจำนวนเต็มสองจำนวนใน Java (และภาษาโปรแกรมอื่น ๆ ส่วนใหญ่) จะปูพื้นผลลัพธ์เสมอ ดังนั้น:

int a, b;
int result = a/b (is the same as floor(a/b) )

แต่เราไม่ต้องการfloor(a/b)แต่ceil(a/b)ใช้คำจำกัดความและแผนการจากWikipedia :ป้อนคำอธิบายภาพที่นี่

ด้วยพล็อตของฟังก์ชันพื้นและเพดานเหล่านี้คุณจะเห็นความสัมพันธ์

ฟังก์ชั่นชั้น ฟังก์ชัน Ceil

floor(x) <= ceil(x)คุณจะเห็นว่า floor(x + s) = ceil(x)เราจำเป็นต้อง sดังนั้นเราจึงต้องไปหา ถ้าเราใช้1/2 <= s < 1มันจะถูกต้อง (ลองใช้ตัวเลขแล้วคุณจะเห็นว่าเป็นเช่นนั้นฉันพบว่าตัวเองยากที่จะพิสูจน์สิ่งนี้) และ1/2 <= (b-1) / b < 1ดังนั้น

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

นี่ไม่ใช่ข้อพิสูจน์ที่แท้จริง แต่ฉันหวังว่าคุณจะพอใจกับมัน หากมีใครสามารถอธิบายได้ดีกว่านี้ฉันก็จะขอบคุณเช่นกัน อาจจะถามมันในMathOverflow


1
จะเป็นประโยชน์อย่างยิ่งหากคุณสามารถอธิบายสัญชาตญาณที่อยู่เบื้องหลังแนวทางที่ใช้งานง่ายน้อยกว่านี้ได้? ฉันรู้ว่านี่ถูกต้องฉันอยากรู้ว่าคุณไปถึงมันได้อย่างไรและฉันจะแสดงว่ามันถูกต้องทางคณิตศาสตร์ได้อย่างไร ฉันลองแก้ทางคณิตศาสตร์แล้วไม่มั่นใจ
Saad Rehman Shah

ฉันหวังว่าคุณจะพอใจกับการแก้ไขของฉันฉันไม่สามารถทำได้ดีกว่านี้อีกแล้วฉันคิดว่า :(
martijnn2008

ฉันถือว่า Math.floor และ ceil ถูกต้องสำหรับการหารจำนวนเต็มเท่านั้นไม่ใช่สำหรับการหารแบบยาวเมื่อค่าถูกร่ายเป็นสองเท่า ตัวอย่างตัวนับคือ 4611686018427386880/4611686018427387137 ล้มเหลวบนพื้นและ 4611686018427386881/4611686018427386880 ล้มเหลวบนเพดาน
Wouter

2
ประเด็นชี้แจง: ผลลัพธ์ของตัวเลือกย่อยสองตัวเลือก 2 ไม่เหมือนกันในทุกกรณี ค่าศูนย์สำหรับ a จะให้ 0 ในครั้งแรกและ 1 ในวินาที (ซึ่งไม่ใช่คำตอบที่ถูกต้องสำหรับแอปพลิเคชันส่วนใหญ่)
Sushisource

1
คุณแน่ใจหรือไม่ว่าคุณไม่ได้หมายถึง "อย่างไรก็ตามโปรดทราบว่ามันใช้ไม่ได้กับ a = 0 และ b < 1"
dantiston

61

157/32 คือint/intซึ่งส่งผลให้เกิดintไฟล์.

ลองใช้ตัวอักษรคู่ - 157/32dซึ่งเป็นซึ่งจะส่งผลให้int/doubledouble


แน่ใจหรือว่า int / int จะทำให้เกิด int เสมอ?! คุณช่วยบอกแหล่งที่มาได้ไหม!


35

157/32คือการหารจำนวนเต็มเนื่องจากตัวอักษรตัวเลขทั้งหมดเป็นจำนวนเต็มเว้นแต่จะระบุไว้เป็นอย่างอื่นด้วยคำต่อท้าย ( dสำหรับคู่lสำหรับ long)

การแบ่งจะถูกปัดเศษลง (เป็น 4) ก่อนที่จะถูกแปลงเป็นสองเท่า (4.0) ซึ่งจะถูกปัดเศษขึ้น (เป็น 4.0)

หากคุณใช้ตัวแปรคุณสามารถหลีกเลี่ยงสิ่งนั้นได้

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);


7

ไม่มีใครพูดถึงสิ่งที่เข้าใจง่ายที่สุด:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

โซลูชันนี้แก้ไขความไม่แม่นยำของการหารสอง


1
Math.round คืนผลยาว
Zulqurnain Jutt

ขอบคุณ @ZulqurnainJutt เพิ่มนักแสดง
IG Pascual


3

เมื่อหารจำนวนเต็มสองจำนวนเช่น

int c = (int) a / (int) b;

ผลลัพธ์คือintค่าที่aหารด้วยbปัดเศษเข้าหาศูนย์ เพราะปัดแล้วผลceil()ไม่ได้ทำอะไร โปรดทราบว่าการปัดเศษนี้ไม่เหมือนกับการfloor()ปัดเศษของค่าอินฟินิตี้เชิงลบ ดังนั้น3/2เท่ากับ1(และfloor(1.5)เท่ากับ1.0แต่(-3)/2เท่ากับ-1(แต่floor(-1.5)เท่ากับ-2.0)

นี้เป็นสิ่งสำคัญเพราะถ้าa/bได้เสมอเช่นเดียวfloor(a / (double) b)แล้วคุณก็สามารถใช้ceil()ในการเป็นa/b-( (-a) / b)

ข้อเสนอแนะในการเดินทางceil(a/b)จาก

int n = (a + b - 1) / b;ซึ่งเทียบเท่ากับa / b + (b - 1) / bหรือ(a - 1) / b + 1

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

อือ. หวังว่าจะสมเหตุสมผล ฉันแน่ใจว่ามีวิธีอธิบายที่สง่างามทางคณิตศาสตร์มากกว่านี้


2

นอกจากนี้ในการแปลงตัวเลขจากจำนวนเต็มเป็นจำนวนจริงคุณสามารถเพิ่มจุด:

int total = (int) Math.ceil(157/32.);

และผลลัพธ์ของ (157/32.) ก็จะเป็นจริงเช่นกัน ;)



1

ตรวจสอบคำตอบด้านล่างสำหรับคำถามของคุณ:

int total = (int) Math.ceil(157/32);

ที่นี่คุณควรคูณ Numerator ด้วย 1.0 จากนั้นจะให้คำตอบของคุณ

int total = (int) Math.ceil(157*1.0/32);


0

Java จัดเตรียมเฉพาะการแบ่งชั้น/ตามค่าเริ่มต้น แต่เราสามารถเขียนเพดานในแง่ของชั้น มาดูกัน:

จำนวนเต็มใด ๆสามารถเขียนแบบที่y y == q*k+rตามคำนิยามของการแบ่งชั้น (ที่นี่floor) ซึ่งรอบนอกr,

floor(q*k+r, k) == q  , where 0 ≤ r ≤ k-1

และในส่วนของเพดาน (ที่นี่ceil) ซึ่งรอบขึ้นr₁,

ceil(q*k+r₁, k) == q+1  , where 1 ≤ r₁ ≤ k

ที่เราสามารถใช้แทนr+1สำหรับr₁:

ceil(q*k+r+1, k) == q+1  , where 0 ≤ r ≤ k-1


จากนั้นเราแทนที่สมการแรกเป็นสมการที่สามเพื่อqรับ

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0 ≤ r ≤ k-1

ในที่สุดได้รับจำนวนเต็มใด ๆyที่y = q*k+r+1บางq, k, rเรามี

ceil(y, k) == floor(y-1, k) + 1

และเราทำเสร็จแล้ว หวังว่านี่จะช่วยได้


ฉันแน่ใจว่าถูกต้อง แต่เนื่องจากประเด็นนี้คือการชี้แจงฉันจึงไม่ชัดเจนว่าเหตุใดจึงceilนิยามเช่นนี้จากคำจำกัดความที่เข้าใจง่ายโดยเฉพาะอย่างยิ่งเมื่อเรานำค่าเพดานของจำนวนเต็มคือ r1 = k เนื่องจากกรณีขอบเป็นสิ่งที่ยุ่งยากเกี่ยวกับเรื่องนี้ฉันคิดว่ามันต้องสะกดให้มากขึ้น
Luigi Plinge

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

0

มีสองวิธีที่คุณสามารถปัดเศษค่าสองเท่าได้

  1. Math.ceil
  2. คณิตศาสตร์ชั้น

หากคุณต้องการคำตอบของคุณ 4.90625 เป็น 4 คุณควรใช้ Math.floor และหากคุณต้องการคำตอบของคุณ 4.90625 เป็น 5 คุณสามารถใช้ Math.ceil

คุณสามารถอ้างอิงรหัสต่อไปนี้ได้

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}

0

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

final var dividend = BigDecimal.valueOf(157);
final var divisor = BigDecimal.valueOf(32);
final var result = dividend.divide(divisor, RoundingMode.CEILING).intValue();

-3
int total = (157-1)/32 + 1

หรือทั่วไปมากขึ้น

(a-1)/b +1 

ฉันคิดว่ามันใช้ได้ แต่คุณไม่ได้อธิบายจริงๆว่าทำไมเวอร์ชันดั้งเดิมถึงใช้ไม่ได้
Teepeemm

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