Mengapa membagi dua int tidak menghasilkan nilai yang tepat ketika ditetapkan menjadi dua kali lipat?

110

Kok bisa di cuplikan berikut

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

cakhirnya memiliki nilai 2, bukan 2,3333, seperti yang diharapkan. Jika adan badalah dobel, jawabannya berubah menjadi 2.333. Tapi tentunya karena c sudah ganda seharusnya bekerja dengan bilangan bulat?

Jadi kenapa int/int=doubletidak berhasil?

Jahoe
sumber
Kemungkinan duplikat hasil Divisi selalu nol
phuclv

Jawaban:

161

Ini karena Anda menggunakan versi pembagian integer operator/, yang membutuhkan waktu 2 intdetik dan mengembalikan sebuah int. Untuk menggunakan doubleversi, yang mengembalikan a double, setidaknya salah satu dari ints harus secara eksplisit dicor ke a double.

c = a/(double)b;
Chad La Guardia
sumber
9
Saya lebih suka untuk secara eksplisit mengkonversi baik adan buntuk doublehanya untuk kejelasan, tapi itu benar-benar tidak peduli.
John Dibling
31
Karena pertanyaannya diberi tag C ++, saya lebih suka melihat static_cast <> daripada C cast.
Martin York
16
Secara pribadi, saya merasa bahwa gaya cast C lebih jelas (casting di sebagian besar bahasa umum lainnya dilakukan dengan cara gaya C). static_cast<>selalu tampak panjang lebar bagiku. Dalam kasus primitif, sebenarnya tidak ada bahaya tercampur static_cast<>dan reinterpret_cast<>bercampur.
Chad La Guardia
6
@ Tux-D: Untuk pemain aritmatika? Saya lebih suka menghindari static_castdalam kasus ini dan menggunakan cor gaya-C sebagai gantinya. Tidak ada manfaat dalam menggunakan cast gaya C ++ di sini dan mereka lebih mengacaukan kode daripada cast gaya C. Gips aritmatika adalah konteks yang tepat di mana gips gaya-C sangat tepat dan sebenarnya lebih sesuai daripada gips lainnya.
AnT
19
Kadang-kadang Anda bisa mengecoh orang-orang "no C-style-cast" dengan menulis double(b). Mereka tidak selalu menyadari bahwa ini adalah konversi, karena terlihat sama dengan panggilan konstruktor eksplisit.
Steve Jessop
12

Ini dia:

a) Membagi dua ints selalu melakukan pembagian bilangan bulat. Jadi hasil dari a/bkasus Anda hanya dapat berupa int.

Jika Anda ingin mempertahankan adan bsebagai ints, namun membaginya sepenuhnya, Anda harus membuang setidaknya satu untuk menggandakan: (double)a/batau a/(double)batau (double)a/(double)b.

b) cadalah double, sehingga dapat menerima sebuah intnilai pada assignement: yang intsecara otomatis dikonversi ke doubledan ditugaskan untuk c.

c) Ingatlah bahwa pada penugasan, ekspresi di sebelah kanan =dihitung pertama (menurut aturan (a) di atas, dan tanpa memperhatikan variabel di sebelah kiri =) dan kemudian ditetapkan ke variabel di sebelah kiri =(menurut ( b) di atas). Saya yakin ini melengkapi gambarannya.

nplatis.dll
sumber
11

Dengan sedikit pengecualian (saya hanya dapat memikirkan satu), C ++ menentukan seluruh arti dari ekspresi (atau sub-ekspresi) dari ekspresi itu sendiri. Apa yang Anda lakukan dengan hasil ekspresi tidak menjadi masalah. Dalam kasus Anda, dalam ekspresi a / b, tidak ada yang doubleterlihat; semuanya int. Jadi kompilator menggunakan pembagian integer. Hanya setelah hasilnya, barulah ia mempertimbangkan apa yang harus dilakukan dengannya, dan mengubahnya menjadi double.

James Kanze
sumber
3
Satu-satunya pengecualian yang dapat saya pikirkan adalah memilih fungsi yang kelebihan beban saat mengambil penunjuk - nilainya &funcnamebergantung pada jenis yang Anda gunakan.
Steve Jessop
2
@ Steve Jessop Itulah satu-satunya pengecualian yang dapat saya pikirkan juga. (Tetapi mengingat ukuran dan kompleksitas standar, saya tidak ingin bersumpah bahwa saya tidak melewatkan satu pun.)
James Kanze
6

cadalah doublevariabel, tetapi nilai yang diberikan padanya adalah intnilai karena dihasilkan dari pembagian dua ints, yang menghasilkan "pembagian bilangan bulat" (menghilangkan sisanya). Jadi yang terjadi di garis c=a/badalah

  1. a/b dievaluasi, membuat jenis sementara int
  2. nilai sementara ditetapkan csetelah konversi ke jenis double.

Nilai a/bditentukan tanpa mengacu pada konteksnya (penugasan ke double).

Fred Foo
sumber
6

Saat Anda membagi dua bilangan bulat, hasilnya akan menjadi bilangan bulat, terlepas dari fakta bahwa Anda menyimpannya dalam bentuk ganda.

Alok Save
sumber
5

Dalam bahasa C ++, hasil subekspresi tidak pernah terpengaruh oleh konteks sekitarnya (dengan beberapa pengecualian yang jarang terjadi). Ini adalah salah satu prinsip yang diikuti oleh bahasa dengan cermat. Ekspresi c = a / bberisi subekspresi independen a / b, yang ditafsirkan secara independen dari apa pun di luar subekspresi tersebut. Bahasa tidak peduli bahwa Anda nanti akan menetapkan hasilnya ke double.a / badalah divisi integer. Ada lagi yang tidak penting. Anda akan melihat prinsip ini diikuti di banyak sudut spesifikasi bahasa. Itulah cara kerja C ++ (dan C).

Salah satu contoh pengecualian yang saya sebutkan di atas adalah penugasan / inisialisasi penunjuk fungsi dalam situasi dengan kelebihan beban

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

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

Ini adalah satu konteks di mana sisi kiri penugasan / inisialisasi memengaruhi perilaku sisi kanan. (Juga, inisialisasi referensi-ke-larik mencegah kerusakan jenis larik, yang merupakan contoh lain dari perilaku serupa.) Dalam semua kasus lain, sisi kanan mengabaikan sisi kiri sepenuhnya.

Semut
sumber
4

The /operator dapat digunakan untuk pembagian integer atau divisi floating point. Anda memberinya dua operan integer, sehingga melakukan pembagian integer dan hasilnya disimpan dalam double.

Vicky
sumber
2

Ini secara teknis bergantung pada bahasa, tetapi hampir semua bahasa memperlakukan subjek ini dengan sama. Jika ada jenis ketidakcocokan antara dua tipe data dalam ekspresi, sebagian besar bahasa akan mencoba mentransmisikan data di satu sisi= untuk mencocokkan data di sisi lain sesuai dengan sekumpulan aturan yang telah ditentukan.

Saat membagi dua bilangan dengan tipe yang sama (integer, double, dll.), Hasilnya akan selalu sama (jadi 'int / int' akan selalu menghasilkan int).

Dalam hal ini Anda memiliki double var = integer result yang melemparkan hasil integer menjadi ganda setelah perhitungan dalam hal ini data pecahan sudah hilang. (kebanyakan bahasa akan melakukan pengecoran ini untuk mencegah ketidakakuratan jenis tanpa menimbulkan pengecualian atau kesalahan).

Jika Anda ingin mempertahankan hasil ganda, Anda akan ingin menciptakan situasi yang Anda miliki double var = double result

Cara termudah untuk melakukannya adalah dengan memaksa ekspresi di sisi kanan persamaan menjadi dua kali lipat:

c = a/(double)b

Pembagian antara integer dan double akan menghasilkan transmisi integer ke double (perhatikan bahwa ketika melakukan matematika, compiler akan sering "upcast" ke tipe data yang paling spesifik untuk mencegah kehilangan data).

Setelah upcast, aakan berakhir sebagai double dan sekarang Anda memiliki pembagian antara dua double. Ini akan membuat divisi dan tugas yang diinginkan.

LAGI, harap dicatat bahwa ini khusus bahasa (dan bahkan dapat menjadi spesifik kompiler), namun hampir semua bahasa (tentu saja semua yang dapat saya pikirkan di luar kepala saya) memperlakukan contoh ini secara identik.

matthewdunnam
sumber
Pertanyaan ini diberi tag [C ++], dan C ++ Standard menentukan dengan tepat cara kerjanya. Tidak yakin apa yang Anda maksud dengan "khusus bahasa" dan ini tentu saja tidak khusus untuk kompilator, dengan asumsi tidak ada ekstensi kompilator yang digunakan.
John Dibling
Juga tidak benar untuk mengatakan bahwa "double var = integer result yang menurunkan double var ke int". Ganda tidak dilemparkan ke int. Hasil int diubah menjadi ganda.
John Dibling
Saya mengizinkan kemungkinan ekstensi kompiler (saya benar-benar pernah mengalami masalah ini di mana lingkungan saya "salah mentransmisikan" hasil dan saya tidak tahu mengapa). Dan hasilnya adalah bahasa tertentu karena di beberapa bahasa tidak mengikuti aturan casting yang sama. Saya tidak menganggap bahwa itu adalah tag khusus C ++. Anda benar tentang komentar "double var = integer result". Diedit untuk mencerminkan itu. Terima kasih!
matthewdunnam
0

Yang penting salah satu elemen perhitungannya adalah tipe float-double. Kemudian untuk mendapatkan hasil ganda, Anda perlu memasukkan elemen ini seperti yang ditunjukkan di bawah ini:

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

atau c = a / static_cast (b);

Atau Anda dapat membuatnya secara langsung ::

c = 7.0 / 3;

Perhatikan bahwa salah satu elemen kalkulasi harus memiliki '.0' untuk menunjukkan pembagian tipe float-double dengan integer. Jika tidak, meskipun variabel c adalah double, hasilnya juga akan nol.

TheArquitect
sumber
Apa jawaban Anda sehingga tidak satu pun dari 9 jawaban lainnya yang belum ada?
bolov