เหตุใดการหารสอง int จึงไม่ให้ค่าที่เหมาะสมเมื่อกำหนดให้เป็นสองเท่า


110

เป็นอย่างไรบ้างในตัวอย่างต่อไปนี้

int a = 7;
int b = 3;
double c = 0;
c = a / b;

cลงเอยด้วยค่า 2 แทนที่จะเป็น 2.3333 อย่างที่คาดหวัง ถ้าaและbเป็นสองเท่าคำตอบจะเปลี่ยนเป็น 2.333 แต่แน่นอนเพราะc มันเป็นสองเท่ามันควรจะทำงานร่วมกับจำนวนเต็ม?

แล้วทำไมถึงint/int=doubleไม่ได้ผล?


ซ้ำเป็นไปได้ของผลหารเป็นศูนย์เสมอ
phuclv

คำตอบ:


161

เนื่องจากคุณกำลังใช้เวอร์ชันการหารจำนวนเต็มoperator/ซึ่งใช้เวลา 2 intวินาทีและส่งกลับintไฟล์. ในการใช้doubleเวอร์ชันซึ่งส่งคืน a doubleอย่างน้อยหนึ่งในนั้นintต้องถูกโยนอย่างชัดเจนไปยังไฟล์double.

c = a/(double)b;

9
ฉันต้องการแปลงทั้งสองอย่างชัดเจนaและbเป็นdoubleเพียงเพื่อความชัดเจน แต่จริงๆแล้วมันไม่สำคัญ
John Dibling

31
เนื่องจากคำถามถูกแท็ก C ++ ฉันจึงอยากเห็น static_cast <> มากกว่าการแคสต์ C
Martin York

16
โดยส่วนตัวแล้วฉันรู้สึกว่าการแคสต์สไตล์ C นั้นชัดเจนกว่า (การแคสต์ในภาษาอื่น ๆ ส่วนใหญ่จะทำในรูปแบบ C) static_cast<>ดูเหมือนจะยืดยาวสำหรับฉันเสมอ ในกรณีของสิ่งดั้งเดิมไม่มีอันตรายใด ๆ ที่จะเกิดขึ้นstatic_cast<>และreinterpret_cast<>ปะปนกัน
Chad La Guardia

6
@ Tux-D: สำหรับการร่ายเลขคณิต? ฉันต้องการหลีกเลี่ยงstatic_castในกรณีนี้และใช้การแสดงแบบ C แทน ไม่มีประโยชน์ในการใช้การแคสต์สไตล์ C ++ ที่นี่และมันทำให้โค้ดยุ่งเหยิงมากกว่าการแคสต์สไตล์ C การร่ายเลขคณิตเป็นบริบทที่การร่ายแบบ C มีความเหมาะสมอย่างสมบูรณ์และเหมาะสมกว่าการร่ายแบบอื่น ๆ
AnT

19
บางครั้งคุณสามารถชิงไหวชิงพริบ "ไม่ C-สไตล์หล่อ" double(b)คนโดยการเขียน พวกเขาไม่ทราบเสมอไปว่ามันเป็นการแปลงเนื่องจากดูเหมือนว่าเป็นการเรียกตัวสร้างที่ชัดเจน
สตีฟเจสซอป

12

นี่คือ:

a) การหารสองints จะทำการหารจำนวนเต็มเสมอ ดังนั้นผลลัพธ์a/bในกรณีของคุณจึงเป็นได้แค่intไฟล์.

หากคุณต้องการที่จะให้aและbเป็นints ยังแบ่งพวกเขาอย่างเต็มที่คุณต้องโยนที่หนึ่งที่น้อยที่สุดของพวกเขาจะเป็นสองเท่า: (double)a/bหรือหรือa/(double)b(double)a/(double)b

b) cคือdoubleเพื่อที่จะสามารถยอมรับintค่า assignement ที่: intถูกแปลงโดยอัตโนมัติและมอบหมายให้doublec

c) จำไว้ว่าในการกำหนดนิพจน์ทางด้านขวาของ=จะถูกคำนวณก่อน (ตามกฎ (a) ด้านบนและไม่คำนึงถึงตัวแปรทางด้านซ้ายของ=) จากนั้นกำหนดให้กับตัวแปรทางด้านซ้ายของ=(ตาม ( b) ด้านบน) ฉันเชื่อว่านี่จะทำให้ภาพสมบูรณ์


11

มีข้อยกเว้นน้อยมาก (ฉันคิดได้เพียงข้อเดียว) C ++ จะกำหนดความหมายทั้งหมดของนิพจน์ (หรือนิพจน์ย่อย) จากนิพจน์นั้นเอง สิ่งที่คุณทำกับผลลัพธ์ของนิพจน์ไม่สำคัญ ในกรณีของคุณในสำนวนa / bไม่มีอะไรให้doubleเห็น intทุกอย่างเป็น ดังนั้นคอมไพเลอร์จึงใช้การหารจำนวนเต็ม doubleเพียงครั้งเดียวก็มีผลที่ไม่ได้พิจารณาว่าจะทำอย่างไรกับมันและแปลงเป็น


3
ข้อยกเว้นอย่างหนึ่งที่ฉันคิดได้คือการเลือกฟังก์ชันโอเวอร์โหลดเมื่อใช้ตัวชี้ - ค่าของ&funcnameขึ้นอยู่กับประเภทที่คุณส่งไป
Steve Jessop

2
@Steve Jessop นั่นเป็นข้อยกเว้นเดียวที่ฉันคิดได้เช่นกัน (แต่ด้วยขนาดและความซับซ้อนของมาตรฐานฉันไม่อยากจะสาบานว่าฉันไม่พลาดเลย)
James Kanze

6

cเป็นdoubleตัวแปร แต่ค่าที่กำหนดให้เป็นintค่าเนื่องจากเป็นผลมาจากการหารสองints ซึ่งทำให้คุณมี "การหารจำนวนเต็ม" (การทิ้งส่วนที่เหลือ) ดังนั้นสิ่งที่เกิดขึ้นในบรรทัดc=a/bคือ

  1. a/b ได้รับการประเมินสร้างประเภทชั่วคราว int
  2. ค่าของชั่วคราวได้รับมอบหมายให้หลังจากการแปลงที่จะพิมพ์cdouble

ค่าของa/bถูกกำหนดโดยไม่ต้องอ้างอิงกับบริบท (การกำหนดให้กับdouble )


6

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


5

ในภาษา C ++ ผลลัพธ์ของ subexpresison จะไม่ได้รับผลกระทบจากบริบทโดยรอบ (โดยมีข้อยกเว้นที่หายาก) นี่เป็นหนึ่งในหลักการที่ใช้ภาษาอย่างระมัดระวัง นิพจน์c = a / bประกอบด้วยนิพจน์ย่อยอิสระa / bซึ่งตีความโดยไม่ขึ้นกับสิ่งที่อยู่ภายนอกนิพจน์ย่อยนั้น ภาษาไม่สนใจว่าคุณจะกำหนดผลลัพธ์เป็นdoubleไฟล์.a / bคือการหารจำนวนเต็ม สิ่งอื่นใดไม่สำคัญ คุณจะเห็นหลักการนี้ตามมาในหลาย ๆ มุมของข้อกำหนดภาษา นั่นเป็นวิธีการทำงานของ C ++ (และ C)

ตัวอย่างหนึ่งของข้อยกเว้นที่ฉันกล่าวถึงข้างต้นคือการกำหนด / การเริ่มต้นตัวชี้ฟังก์ชันในสถานการณ์ที่มีฟังก์ชันมากเกินไป

void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

นี่เป็นบริบทหนึ่งที่ด้านซ้ายมือของงาน / การกำหนดค่าเริ่มต้นมีผลต่อพฤติกรรมของด้านขวามือ (นอกจากนี้การเริ่มต้นการอ้างอิงไปยังอาร์เรย์จะป้องกันการสลายตัวของประเภทอาร์เรย์ซึ่งเป็นอีกตัวอย่างหนึ่งของลักษณะการทำงานที่คล้ายคลึงกัน) ในกรณีอื่น ๆ ทางด้านขวาจะละเว้นด้านซ้ายโดยสิ้นเชิง


4

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


2

ในทางเทคนิคขึ้นอยู่กับภาษา แต่เกือบทุกภาษาจะปฏิบัติต่อเรื่องนี้เหมือนกัน เมื่อมีประเภทข้อมูลสองประเภทในนิพจน์ไม่ตรงกันภาษาส่วนใหญ่จะพยายามแคสต์ข้อมูลที่ด้านหนึ่งของข้อมูล=เพื่อให้ตรงกับข้อมูลอีกด้านหนึ่งตามชุดของกฎที่กำหนดไว้ล่วงหน้า

เมื่อหารสองจำนวนประเภทเดียวกัน (จำนวนเต็มคู่ผสม ฯลฯ ) ผลลัพธ์จะเป็นประเภทเดียวกันเสมอ (ดังนั้น 'int / int' จะให้ผลลัพธ์เป็น int เสมอ)

ในกรณีนี้คุณจะ double var = integer result ร่ายผลลัพธ์จำนวนเต็มให้เป็นสองเท่าหลังจากการคำนวณซึ่งในกรณีนี้ข้อมูลเศษส่วนจะสูญหายไปแล้ว (ภาษาส่วนใหญ่จะทำการแคสต์นี้เพื่อป้องกันความไม่ถูกต้องของประเภทโดยไม่เพิ่มข้อยกเว้นหรือข้อผิดพลาด)

หากคุณต้องการให้ผลลัพธ์เป็นสองเท่าคุณจะต้องสร้างสถานการณ์ที่คุณมี double var = double result

วิธีที่ง่ายที่สุดคือบังคับให้นิพจน์ทางด้านขวาของสมการร่ายเป็นสองเท่า:

c = a/(double)b

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

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

อีกครั้งโปรดทราบว่านี่เป็นภาษาเฉพาะ (และอาจเป็นภาษาเฉพาะของคอมไพเลอร์ก็ได้) อย่างไรก็ตามเกือบทุกภาษา (แน่นอนว่าทุกภาษาที่ฉันคิดออกจากหัวของฉัน) ปฏิบัติต่อตัวอย่างนี้เหมือนกัน


คำถามนี้ติดแท็ก [C ++] และ C ++ Standard จะกำหนดวิธีการทำงาน ไม่แน่ใจว่าคุณหมายถึงอะไรโดย "เฉพาะภาษา" และแน่นอนว่าไม่ใช่เฉพาะคอมไพเลอร์โดยสมมติว่าไม่มีส่วนขยายของคอมไพเลอร์เกี่ยวข้อง
John Dibling

นอกจากนี้ยังไม่ถูกต้องที่จะบอกว่า "ผลลัพธ์ var = จำนวนเต็มสองเท่าซึ่งทำให้ตัวแปรคู่ลงไป int" คู่ไม่ถูกโยนไปที่ int ผลลัพธ์ int ถูกแปลงเป็นสองเท่า
John Dibling

ฉันอนุญาตให้มีความเป็นไปได้ของส่วนขยายของคอมไพเลอร์ (ฉันเคยมีปัญหานี้จริงครั้งหนึ่งที่สภาพแวดล้อมของฉัน "ส่งผิด" ผลลัพธ์และฉันไม่สามารถหาสาเหตุได้) และผลลัพธ์จะเป็นภาษาเฉพาะเนื่องจากในบางภาษาไม่เป็นไปตามกฎการแคสต์เดียวกัน ฉันไม่ได้พิจารณาว่าเป็นแท็กเฉพาะ C ++ คุณพูดถูกเกี่ยวกับความคิดเห็น "double var = integer result" แก้ไขเพื่อสะท้อนให้เห็นว่า ขอบคุณ!
matthewdunnam

0

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

c = static_cast<double>(a) / b;

หรือ c = a / static_cast (b);

หรือจะสร้างโดยตรงก็ได้ ::

c = 7.0 / 3;

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


คำตอบของคุณนำมาซึ่งอะไรที่ไม่มีคำตอบอีก 9 ข้อที่ยังไม่มีอยู่
bolov

0

ด้วยเหตุผลเดียวกันข้างต้นคุณจะต้องแปลง 'a' หรือ 'b' ตัวใดตัวหนึ่งเป็นสองประเภท อีกวิธีหนึ่งคือการใช้:

double c = (a+0.0)/b;

เศษเป็น (โดยปริยาย) แปลงเป็นคู่เพราะเราได้เพิ่มคู่มันคือ0.0

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