การแบ่งจำนวนเต็ม: คุณสร้างจำนวนอย่างไร


249

สำหรับบล็อกรหัสนี้:

int num = 5;
int denom = 7;
double d = num / denom;

ค่าของการมีd 0.0สามารถบังคับให้ทำงานโดยการคัดเลือกนักแสดง:

double d = ((double) num) / denom;

แต่มีวิธีอื่นในการรับdoubleผลลัพธ์ที่ถูกต้องหรือไม่ ฉันไม่ชอบการคัดเลือกแบบดั้งเดิมผู้รู้ว่าจะเกิดอะไรขึ้น



9
การหล่อ 'int' เป็นสองเท่านั้นปลอดภัยคุณจะได้รับค่าเดิมเสมอโดยไม่สูญเสียความแม่นยำ
Peter Lawrey

1
ฉันต้องการทราบว่าต่อไปนี้เป็นขั้นตอนที่ถูกต้องที่คอมไพเลอร์สำหรับการแบ่งหรือไม่: 1) นำ NUM ไปลอย 2) โยน denom เพื่อลอยเช่นกัน 2) หาร NUM ด้วย denom โปรดแจ้งให้เราทราบหากฉันไม่ถูกต้อง
mannyee

คำตอบ:


156
double num = 5;

ที่หลีกเลี่ยงการโยน แต่คุณจะพบว่าการแปลงสถานะมีความหมายชัดเจน คุณไม่ต้องคาดเดาเพียงตรวจสอบJLS int เป็นสองเท่าคือการแปลงแบบขยาย จาก§5.1.2 :

การแปลงแบบดั้งเดิมที่กว้างขึ้นจะไม่สูญเสียข้อมูลเกี่ยวกับขนาดโดยรวมของค่าตัวเลข

[ ... ]

การแปลงค่า int หรือค่ายาวเป็นทศนิยมหรือค่ายาวเป็นสองเท่าอาจส่งผลให้สูญเสียความแม่นยำ - นั่นคือผลลัพธ์อาจสูญเสียบิตที่มีนัยสำคัญน้อยที่สุด ในกรณีนี้ส่งผลให้ค่าจุดลอยตัวจะมีรุ่นที่โค้งมนอย่างถูกต้องของค่าจำนวนเต็มโดยใช้มาตรฐาน IEEE 754 รอบเพื่อที่ใกล้ที่สุดโหมด(§4.2.4)

5 สามารถแสดงว่าเป็นสองเท่า


60
+1 หยุดกลัวการคัดเลือกนักแสดง เรียนรู้วิธีการใช้งาน มันคือทั้งหมดที่กำหนดไว้อย่างดี
Mark Peters

1
@FabricioPH สิ่งนี้ใช้ได้ในทุกสถานการณ์และเหมือนกันกับโซลูชัน * 1.0
Vitruvius

3
@Saposhiente มันทำงานได้มากกว่าหรือไม่ การเปลี่ยนประเภทของตัวแปรนั้นดูเหมือนว่าเป็นโค้ดสกปรกสำหรับฉัน หากคุณมีบางอย่างที่ต้องเป็นจำนวนเต็มและคุณเปลี่ยนการแทนทุ่นเพื่อให้สามารถทำการคำนวณทางคณิตศาสตร์ได้คุณอาจเสี่ยงตัวเอง ในบางบริบทการอ่านตัวแปรเป็นจำนวนเต็มทำให้โค้ดเข้าใจง่ายขึ้น
Fabricio PH

2
@ FabricioPH การคูณโดย1.0ยังคงเปลี่ยนประเภทของผลลัพธ์ในลักษณะที่ต่างออกไป 'ดูเหมือนโค้ดสกปรกสำหรับฉัน' ไม่ได้อธิบายในสถานการณ์ที่คุณเชื่อว่าการคูณด้วย1.0ดีกว่าจริง ๆ (หรือสาเหตุที่อาจเป็น)
Matthew Flaschen

4
@FabricioPH ดูเหมือนว่าคุณและ OP จะทำงานหนักภายใต้ความเชื่อที่ผิด ๆ (ซึ่งคุณพูดว่า "การเปลี่ยนชนิดของตัวแปร") ที่(double)numเปลี่ยนแปลงnumไป มันไม่ - มันสร้าง double ชั่วคราวสำหรับบริบทของคำสั่ง
พอลทอมบลิน

100

มีอะไรผิดปกติในการคัดเลือกนักแสดง

หากคุณไม่ต้องการที่จะโยนด้วยเหตุผลบางอย่างคุณสามารถทำได้

double d = num * 1.0 / denom;

63
... ซึ่งจะทำการบล็อกโดยนัยก่อนการคูณ
Alice Purcell

2
ในกรณีส่วนใหญ่จะเป็นการดีกว่าการเปลี่ยนประเภทของตัวแปรอื่น ๆ
Fabricio PH

3
@FabricioPH ไม่มีสิ่งใดที่บ่งบอกว่าสิ่งนี้เป็นจริงเว้นแต่คุณจะมีแหล่งอ้างอิง
Vitruvius

1
@Saposhiente ตอบในความคิดเห็นอื่น ๆ ที่คล้ายกันของคุณ
Fabricio PH

1
คุณสามารถใช้ postfix "D" (เพื่อทำการส่งโดยนัยเดียวกัน) - ตัวอย่างเช่น:double d = num * 1D / denom;
BrainSlugs83

73

ฉันไม่ชอบการคัดเลือกแบบดั้งเดิมผู้รู้ว่าจะเกิดอะไรขึ้น

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

คุณสามารถกำจัดวงเล็บคู่พิเศษโดยการเลือกตัวหารแทนตัวเศษ:

double d = num / (double) denom;

2
คุณสามารถทำได้เช่นกันdouble d = (double) num / denom;... (ตกลงอันนี้ขึ้นอยู่กับความสำคัญ)
user85421

27

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

double d = 5 / (double) 20; //cast to double, to do floating point calculations

โปรดทราบว่าการส่งผลลัพธ์จะไม่สามารถทำได้

double d = (double)(5 / 20); //produces 0.0

17

โยนหนึ่งในจำนวนเต็ม / ทั้งจำนวนเต็มเพื่อลอยเพื่อบังคับให้การดำเนินการเสร็จสิ้นด้วยคณิตศาสตร์จุดลอย มิฉะนั้นต้องการเลขจำนวนเต็มเสมอ ดังนั้น:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

โปรดทราบว่าการส่งผลลัพธ์จะไม่สามารถทำได้ เพราะการแบ่งแรกจะทำตามกฎที่มีมาก่อน

double d = (double)(5 / 20); //produces 0.0

ฉันไม่คิดว่าจะมีปัญหาอะไรกับการคัดเลือกนักแสดงอย่างที่คุณคิด


10

การหล่อประเภทเป็นวิธีเดียว


การผลิตdoubleจากการหารจำนวนเต็ม - ไม่มีวิธีอื่นโดยไม่มีการคัดเลือก (อาจเป็นคุณจะไม่ทำอย่างชัดเจน แต่จะเกิดขึ้น)

ตอนนี้มีหลายวิธีที่เราสามารถพยายามที่จะได้รับอย่างแม่นยำdoubleคุ้มค่า (ที่numและdenomมีintชนิดและแน่นอนกับการคัดเลือกนักแสดง ) -

  1. ด้วยการคัดเลือกอย่างชัดเจน:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

แต่ไม่ double d = (double) (num / denom);

  1. ด้วยการคัดเลือกโดยนัย:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

แต่ไม่ใช่double d = num / denom * 1.0;
และไม่double d = 0.0 + ( num / denom );


2

ใช้สิ่งที่ชอบ:

double step = 1d / 5;

(1d เป็นนักแสดงที่จะเพิ่มเป็นสองเท่า)


5
1d ไม่ใช่นักแสดง แต่เป็นเพียงการประกาศ
Sefier Tang

0

คุณอาจพิจารณาห่อการดำเนินงาน ตัวอย่างเช่น:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

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



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