การหาร Int: เหตุใดผลลัพธ์ของ 1/3 == 0


107

ฉันกำลังเขียนรหัสนี้:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

ผลลัพธ์คือ 0 เหตุใดจึงเป็นเช่นนี้และฉันจะแก้ปัญหานี้ได้อย่างไร

คำตอบ:


147

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

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

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


33
+1 และมักจะปัดเศษลง int i = .99999999ตั้งค่า int เป็น 0 โดยเฉพาะอย่างยิ่งมันใช้ส่วนจำนวนเต็มและทิ้งส่วนที่เหลือ
Byron Whitlock

37
มันจะปัดเศษ "ไปทางศูนย์" ซึ่งเป็น "ด้านล่าง" สำหรับค่าที่มากกว่าศูนย์ (+0.9 ถูกปัดเป็น 0, -0.9 ปัดเป็น 0 ด้วย)
Adrian Smith

@ ไบรอน: ใช่แน่นอน ฉันไม่เชื่อว่าโปรเซสเซอร์จะทำการปัดเศษใด ๆ เนื่องจากการแบ่งส่วนถูกนำไปใช้แตกต่างกันมาก แต่ก็สะดวกที่จะคิดอย่างนั้น
Noldorin

15
ไม่ไม่ปัดเศษ: การตัดทอน
Basil Bourque

3
@BasilBourque ในศัพท์จาวาการปัดเศษDOWNเป็นศูนย์ การปัดเศษFLOORเป็นไปทางลบอินฟินิตี้
Andreas

23

1/3 ใช้การหารจำนวนเต็มเนื่องจากทั้งสองด้านเป็นจำนวนเต็ม

คุณจำเป็นต้องมีอย่างน้อยหนึ่งของพวกเขาจะเป็นหรือfloatdouble

หากคุณกำลังป้อนค่าในรหัสที่มาเช่นคำถามของคุณคุณสามารถทำ1.0/3; 1.0เป็นคู่

หากคุณได้รับค่าจากที่อื่นคุณสามารถใช้(double)เพื่อเปลี่ยนintเป็นไฟล์double.

int x = ...;
int y = ...;
double value = ((double) x) / y;

10

แสดงเป็นไฟล์ double

double g = 1.0/3.0

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


2

คุณควรใช้

double g=1.0/3;

หรือ

double g=1/3.0;

การหารจำนวนเต็มจะส่งกลับจำนวนเต็ม


2

เนื่องจากคุณกำลังทำการหารจำนวนเต็ม

ดังที่ @Noldorin กล่าวว่าถ้าตัวดำเนินการทั้งสองเป็นจำนวนเต็มระบบจะใช้การหารจำนวนเต็ม

ผลลัพธ์ 0.33333333 ไม่สามารถแสดงเป็นจำนวนเต็มได้ดังนั้นจึงกำหนดเฉพาะส่วนจำนวนเต็ม (0) ให้กับผลลัพธ์

ถ้าตัวดำเนินการตัวใดตัวหนึ่งเป็นdouble/ floatเลขคณิตทศนิยมจะเกิดขึ้น แต่คุณจะมีปัญหาเดียวกันหากคุณทำเช่นนั้น:

int n = 1.0 / 3.0;

1

เนื่องจากถือว่า 1 และ 3 เป็นจำนวนเต็มดังนั้นจึงปัดเศษผลลัพธ์ให้เป็น 0 เพื่อให้เป็นจำนวนเต็ม

เพื่อให้ได้ผลลัพธ์ที่คุณต้องการให้บอก java อย่างชัดเจนว่าตัวเลขเป็นสองเท่าดังนี้:

double g = 1.0/3.0;

1

ทำให้ 1 เป็นลอยและหารลอยจะถูกนำมาใช้

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}

1

การแปลงใน JAVA นั้นค่อนข้างง่าย แต่ต้องมีความเข้าใจ ตามที่อธิบายไว้ใน JLS สำหรับการดำเนินการจำนวนเต็ม :

หากตัวดำเนินการจำนวนเต็มนอกเหนือจากตัวดำเนินการ shift มีตัวถูกดำเนินการประเภทยาวอย่างน้อยหนึ่งตัวการดำเนินการจะดำเนินการโดยใช้ความแม่นยำ 64 บิตและผลลัพธ์ของตัวดำเนินการตัวเลขเป็นประเภทที่ยาว หากตัวถูกดำเนินการอื่นไม่ยาวตัวดำเนินการจะถูกขยายก่อน (§5.1.5) เพื่อพิมพ์แบบยาวตามการเลื่อนตำแหน่งตัวเลข (§5.6)

และตัวอย่างเป็นวิธีที่ดีที่สุดในการแปล JLS เสมอ;)

int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

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

short + int -> int + int -> int

ตัวอย่างเล็ก ๆ ที่ใช้ Eclipse เพื่อแสดงให้เห็นว่าแม้แต่การเพิ่มสองshortวินาทีก็ไม่ง่ายอย่างนั้น:

short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

สิ่งนี้จะต้องใช้การหล่อโดยสูญเสียความแม่นยำที่เป็นไปได้

เช่นเดียวกับตัวดำเนินการจุดลอยตัว

หากตัวถูกดำเนินการอย่างน้อยหนึ่งตัวดำเนินการกับตัวดำเนินการตัวเลขเป็นประเภท double การดำเนินการจะดำเนินการโดยใช้เลขคณิตทศนิยม 64 บิตและผลลัพธ์ของตัวดำเนินการตัวเลขเป็นค่าประเภท double ถ้าตัวถูกดำเนินการอีกตัวไม่ใช่ double จะถูกขยายก่อน (§5.1.5) เพื่อพิมพ์ double by numeric promotion (§5.6)

ดังนั้นการส่งเสริมการขายจะทำในการลอยเป็นสองเท่า

และการผสมของทั้งจำนวนเต็มและค่าลอยทำให้เกิดค่าลอยตามที่กล่าว

หากตัวถูกดำเนินการอย่างน้อยหนึ่งตัวดำเนินการกับตัวดำเนินการไบนารีเป็นประเภทจุดลอยตัวการดำเนินการนั้นจะเป็นการดำเนินการแบบทศนิยมแม้ว่าอีกตัวจะเป็นอินทิกรัล

สิ่งนี้เป็นจริงสำหรับตัวดำเนินการไบนารี แต่ไม่ใช่สำหรับ "ตัวดำเนินการมอบหมาย" เช่น +=

ตัวอย่างการทำงานง่ายๆก็เพียงพอที่จะพิสูจน์สิ่งนี้

int i = 1;
i += 1.5f;

เหตุผลก็คือมีการโยนโดยปริยายที่นี่ซึ่งจะถูกดำเนินการเช่น

i = (int) i + 1.5f
i = (int) 2.5f
i = 2

1

1 และ 3 เป็นจำนวนเต็ม contants และ Java ไม่ส่วนจำนวนเต็มซึ่งเป็นผลเป็น 0 หากคุณต้องการที่จะเขียนค่าคงที่คู่คุณต้องเขียนและ1.03.0


1

วิธีแก้ที่ง่ายที่สุดก็แค่ทำสิ่งนี้

double g = ((double) 1 / 3);

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


1

ฉันทำอย่างนี้.

double g = 1.0/3.0;
System.out.printf("%gf", g);

ใช้. 0 ขณะทำการคำนวณสองครั้งมิฉะนั้น Java จะถือว่าคุณใช้จำนวนเต็ม หากการคำนวณใช้จำนวนค่าสองค่าผลลัพธ์จะเป็นค่าสองเท่า หากเป็นจำนวนเต็มทั้งหมดผลลัพธ์จะเป็นจำนวนเต็ม


0

(1/3) หมายถึงการหารจำนวนเต็มนั่นคือสาเหตุที่คุณไม่สามารถรับค่าทศนิยมจากการหารนี้ได้ ในการแก้ปัญหานี้ให้ใช้:

public static void main(String[] args) {
        double g = 1.0 / 3;
        System.out.printf("%.2f", g);
    }

0
public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

เนื่องจากทั้ง 1 และ 3 เป็น ints ผลลัพธ์จึงไม่ถูกปัดเศษ แต่จะถูกตัดทอน คุณจึงไม่สนใจเศษส่วนและใช้เฉพาะ wholes

เพื่อหลีกเลี่ยงปัญหานี้ให้มีอย่างน้อยหนึ่งในตัวเลข 1 หรือ 3 ของคุณเป็นรูปแบบทศนิยม 1.0 และ / หรือ 3.0


0

ลองสิ่งนี้:

public static void main(String[] args) {
    double a = 1.0;
    double b = 3.0;
    double g = a / b;
    System.out.printf(""+ g);
}

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

-1

ทำ "double g = 1.0 / 3.0;" แทน.


แค่อยากรู้ ... คำตอบนี้ผิดอะไร? ฉันใช้แนวทางนี้เมื่อพบปัญหาครั้งแรก ขอขอบคุณคำอธิบายว่ามีอะไรผิดปกติกับโซลูชันนี้ ขอบคุณล่วงหน้า.
Mr. Port St Joe

@ Mr.PortStJoe ไม่ได้ให้รายละเอียดใด ๆ กับผู้ที่ถามคำถาม เมื่อดูคำตอบอันดับต้น ๆ เราจะเห็นว่าพวกเขาอธิบายว่าทำไมจึงเกิดขึ้นเช่นกัน แม้ว่าคำตอบอาจถูกต้องในทางเทคนิค แต่ประโยชน์อาจถูกมองว่ามี จำกัด
Andrew T Finnell

-1

อีกหลายคนไม่สามารถชี้ให้เห็นปัญหาที่แท้จริง:

การดำเนินการกับจำนวนเต็มเท่านั้นที่จะโยนผลลัพธ์ของการดำเนินการเป็นจำนวนเต็ม

หมายความว่าผลลัพธ์ทศนิยมที่สามารถแสดงเป็นจำนวนเต็มจะถูกตัดทอน (ตัดส่วนทศนิยมออก)

คุณถามอะไรการหล่อ (การพิมพ์ / การแปลงประเภท)?

มันแตกต่างกันไปตามการนำภาษาไปใช้ แต่ Wikipedia มีมุมมองที่ค่อนข้างครอบคลุมและพูดถึงการบีบบังคับด้วยเช่นกันซึ่งเป็นข้อมูลสำคัญในการตอบคำถามของคุณ

http://en.wikipedia.org/wiki/Type_conversion


คำตอบนี้มีข้อบกพร่อง ไม่มีการแคสต์ประเภทหรือการแปลงประเภทที่เกี่ยวข้องกับการกำหนดจำนวนเต็มทั้งหมดเช่น1/2ไม่ใช่ในภาษาเป้าหมาย (java) คุณเพียงแค่เรียกใช้การหารจำนวนเต็มซึ่งจะทำให้ได้ผลลัพธ์เป็นจำนวนเต็ม การแคสต์ประเภทเป็นเพียงเพราะการแปลงขึ้นจากintไปเป็นdoubleระหว่างการมอบหมายงาน
YoYo

@ โยโย่คุณบอกว่า 0.5 เป็นจำนวนเต็ม? ฉันไม่คิดอย่างนั้นขวาง
sova

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