Melihat kode C # ini:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
Hasil dari setiap matematika dilakukan pada byte
(atau short
) tipe secara implisit dilemparkan kembali ke integer. Solusinya adalah dengan secara eksplisit mengembalikan hasilnya ke byte:
byte z = (byte)(x + y); // this works
Apa yang saya pikirkan adalah mengapa? Apakah itu arsitektur? Filosofis?
Kita punya:
int
+int
=int
long
+long
=long
float
+float
=float
double
+double
=double
Jadi mengapa tidak:
byte
+byte
=byte
short
+short
=short
?
Sedikit latar belakang: Saya melakukan daftar panjang perhitungan pada "angka kecil" (yaitu <8) dan menyimpan hasil antara dalam array besar. Menggunakan array byte (bukan array int) lebih cepat (karena hit cache). Tetapi byte-cast yang luas menyebar melalui kode membuatnya lebih tidak terbaca.
c#
type-conversion
Robert Cartaino
sumber
sumber
byte1 | byte2
sama sekali tidak memperlakukan mereka sebagai angka. Ini memperlakukan mereka dengan tepat sebagai pola bit. Saya mengerti sudut pandang Anda, tetapi kebetulan bahwa setiap kali saya melakukan aritmatika pada byte dalam C #, saya benar-benar memperlakukan mereka sebagai bit, bukan angka, dan perilaku ini selalu menghalangi.Jawaban:
Baris ketiga dari cuplikan kode Anda:
sebenarnya berarti
Jadi, tidak ada operasi + pada byte, byte pertama kali dilemparkan ke bilangan bulat dan hasil penambahan dua bilangan bulat adalah bilangan bulat (32-bit).
sumber
int
ini terjadi: stackoverflow.com/a/43578929/4561887Dalam hal "mengapa itu terjadi sama sekali" itu karena tidak ada operator yang didefinisikan oleh C # untuk aritmatika dengan byte, sbyte, pendek atau ushort, seperti yang dikatakan orang lain. Jawaban ini adalah tentang mengapa operator itu tidak didefinisikan.
Saya percaya itu pada dasarnya demi kinerja. Prosesor memiliki operasi asli untuk melakukan aritmatika dengan 32 bit dengan sangat cepat. Melakukan konversi kembali dari hasil ke byte secara otomatis bisa dilakukan, tetapi akan menghasilkan penalti kinerja dalam kasus di mana Anda tidak benar-benar menginginkan perilaku itu.
Saya pikir ini disebutkan dalam salah satu standar C # yang beranotasi. Mencari ...
EDIT: Mengganggu, saya sekarang telah melihat melalui spesifikasi ECMA C # 2 yang beranotasi, spesifikasi MS C # 3 yang beranotasi dan spesifikasi CLI anotasi, dan tidak satupun dari mereka menyebutkan ini sejauh yang saya bisa lihat. Saya yakin saya telah melihat alasan yang diberikan di atas, tetapi saya terkejut jika saya tahu di mana. Permintaan maaf, penggemar referensi :(
sumber
Saya pikir saya pernah melihat ini di suatu tempat sebelumnya. Dari artikel ini, The Old New Thing :
EDIT : Raymond membela, pada dasarnya, pendekatan C dan C ++ awalnya. Dalam komentarnya, dia membela fakta bahwa C # mengambil pendekatan yang sama, dengan alasan kompatibilitas mundur bahasa.
sumber
C #
ECMA-334 menyatakan bahwa penambahan hanya didefinisikan sebagai legal pada int + int, uint + uint, panjang + panjang dan ulong + ulong (ECMA-334 14.7.4). Dengan demikian, ini adalah operasi kandidat yang harus dipertimbangkan sehubungan dengan 14.4.2. Karena ada cast implisit dari byte ke int, uint, long dan ulong, semua anggota fungsi tambahan adalah anggota fungsi yang berlaku di bawah 14.4.2.1. Kita harus menemukan pemeran tersirat terbaik menurut aturan dalam 14.4.2.3:
Casting (C1) ke int (T1) lebih baik daripada casting (C2) ke uint (T2) atau ulong (T2) karena:
Casting (C1) ke int (T1) lebih baik daripada casting (C2) hingga panjang (T2) karena ada pemeran implisit dari int ke panjang:
Karenanya fungsi int + int digunakan, yang mengembalikan int.
Itu semua sangat jauh untuk mengatakan bahwa itu terkubur sangat dalam dalam spesifikasi C #.
CLI
CLI hanya beroperasi pada 6 jenis (int32, int asli, int64, F, O, dan &). (ECMA-335 partisi 3 bagian 1.5)
Byte (int8) bukan salah satu dari jenis itu, dan secara otomatis dipaksa ke int32 sebelum penambahan. (ECMA-335 partisi 3 bagian 1.6)
sumber
byte3 = byte1 And byte2
tanpa gips, tetapi tidak membantu akan melemparkan pengecualian runtime jikaint1 = byte1 + byte2
menghasilkan nilai lebih dari 255. Saya tidak tahu apakah ada bahasa yang mengizinkanbyte3 = byte1+byte2
dan melempar pengecualian ketika itu melebihi 255, tetapi tidak melempar pengecualian jikaint1 = byte1+byte2
hasil nilai dalam kisaran 256-510.Jawaban yang menunjukkan beberapa penambahan byte yang tidak efisien dan memotong hasilnya kembali ke byte salah. Prosesor x86 memiliki instruksi yang dirancang khusus untuk operasi integer pada kuantitas 8-bit.
Bahkan, untuk prosesor x86 / 64, melakukan operasi 32-bit atau 16-bit kurang efisien daripada operasi 64-bit atau 8-bit karena byte awalan operan yang harus diterjemahkan. Pada mesin 32-bit, melakukan operasi 16-bit memerlukan hukuman yang sama, tetapi masih ada opcode khusus untuk operasi 8-bit.
Banyak arsitektur RISC memiliki instruksi efisien kata / byte asli yang serupa. Yang umumnya tidak memiliki nilai store-and-convert-to-signed-of-some-bit-length.
Dengan kata lain, keputusan ini harus didasarkan pada persepsi untuk apa tipe byte, bukan karena inefisiensi yang mendasari perangkat keras.
sumber
byte
(danchar
), tetapi tidak untukshort
yang secara semantik jelas angka.Saya ingat pernah membaca sesuatu dari Jon Skeet (tidak dapat menemukannya sekarang, saya akan terus mencari) tentang bagaimana byte sebenarnya tidak membebani operator +. Bahkan, ketika menambahkan dua byte seperti dalam sampel Anda, setiap byte sebenarnya secara implisit dikonversi ke int. Hasil yang jelas int. Sekarang MENGAPA ini dirancang dengan cara ini, saya akan menunggu Jon Skeet sendiri untuk memposting :)
EDIT: Ditemukan! Info hebat tentang topik ini di sini .
sumber
Ini karena overflow dan carry.
Jika Anda menambahkan dua angka 8 bit, angka tersebut mungkin melimpah ke bit ke-9.
Contoh:
Saya tidak tahu pasti, tapi saya menganggap bahwa
ints
,longs
, dandoubles
diberi ruang lebih karena mereka cukup besar seperti itu. Juga, mereka adalah kelipatan dari 4, yang lebih efisien bagi komputer untuk menangani, karena lebar bus data internal menjadi 4 byte atau 32 bit (64 bit semakin lazim sekarang) lebar. Byte dan pendek sedikit lebih tidak efisien, tetapi mereka dapat menghemat ruang.sumber
Dari spesifikasi bahasa C # 1.6.7.5 7.2.6.2 Promosi numerik biner ini mengubah kedua operan menjadi int jika tidak dapat masuk ke dalam beberapa kategori lain. Dugaan saya adalah mereka tidak membebani operator + untuk mengambil byte sebagai parameter tetapi ingin itu bertindak agak normal sehingga mereka hanya menggunakan tipe data int.
Bahasa C #
sumber
Kecurigaan saya adalah bahwa C # sebenarnya memanggil
operator+
didefinisikan padaint
(yang mengembalikanint
kecuali Anda berada dalamchecked
blok), dan secara implisit pengecoran baik dari Andabytes
/shorts
keints
. Itu sebabnya perilaku itu tampak tidak konsisten.sumber
Ini mungkin keputusan praktis dari para perancang bahasa. Bagaimanapun, int adalah Int32, integer bertanda 32-bit. Setiap kali Anda melakukan operasi integer pada tipe yang lebih kecil dari int, itu akan dikonversi menjadi int 32 bit oleh sebagian besar CPU 32 bit. Itu, dikombinasikan dengan kemungkinan melimpah bilangan bulat kecil, mungkin menyegel kesepakatan itu. Ini menyelamatkan Anda dari tugas untuk terus memeriksa over / under-flow, dan ketika hasil akhir dari ekspresi pada byte berada dalam jangkauan, meskipun pada beberapa tahap menengah itu akan berada di luar jangkauan, Anda mendapatkan yang benar hasil.
Pikiran lain: Over / under-flow pada tipe-tipe ini harus disimulasikan, karena tidak akan terjadi secara alami pada CPU target yang paling mungkin. Kenapa mengganggu?
sumber
Ini untuk sebagian besar jawaban saya yang berkaitan dengan topik ini, diajukan pertama ke pertanyaan serupa di sini .
Semua operasi dengan angka integral lebih kecil dari Int32 dibulatkan hingga 32 bit sebelum kalkulasi secara default. Alasan mengapa hasilnya adalah Int32 hanya untuk membiarkannya karena setelah perhitungan. Jika Anda memeriksa opcodes aritmatika MSIL, satu-satunya tipe numerik integral yang mereka gunakan adalah Int32 dan Int64. Ini "dengan desain".
Jika Anda menginginkan hasilnya kembali dalam format Int16, itu tidak relevan jika Anda melakukan kode yang dimasukkan, atau kompiler (secara hipotetis) memancarkan konversi "di bawah tenda".
Misalnya, untuk melakukan aritmatika Int16:
Dua angka akan berkembang menjadi 32 bit, ditambahkan, kemudian dipotong kembali menjadi 16 bit, seperti yang dimaksudkan MS.
Keuntungan menggunakan short (atau byte) terutama penyimpanan dalam kasus di mana Anda memiliki sejumlah besar data (data grafis, streaming, dll.)
sumber
Tambahan tidak didefinisikan untuk byte. Jadi mereka dilemparkan ke int untuk penambahan. Ini berlaku untuk sebagian besar operasi matematika dan byte. (perhatikan ini adalah bagaimana dulu dalam bahasa yang lebih tua, saya mengasumsikan bahwa itu berlaku hari ini).
sumber
Saya pikir ini adalah keputusan desain tentang operasi mana yang lebih umum ... Jika byte + byte = byte mungkin lebih banyak orang akan terganggu dengan harus melakukan cast ke int ketika sebuah int diperlukan sebagai hasilnya.
sumber
Dari kode .NET Framework:
Sederhanakan dengan .NET 3.5 dan di atasnya:
sekarang kamu bisa melakukan:
sumber
Saya sudah menguji kinerja antara byte dan int.
Dengan nilai int:
Dengan nilai byte:
Berikut hasilnya:
byte: 3.57s 157mo, 3.71s 171mo, 3.74s 168mo dengan CPU ~ = 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo dengan CPU ~ = 27%
Kesimpulan:
byte menggunakan lebih banyak CPU tetapi les memori dan lebih cepat (mungkin karena ada byte lebih sedikit untuk dialokasikan)
sumber
Selain semua komentar hebat lainnya, saya pikir saya akan menambahkan satu berita kecil. Banyak komentar bertanya-tanya mengapa int, panjang, dan hampir semua tipe numerik lainnya tidak juga mengikuti aturan ini ... mengembalikan tipe "lebih besar" sebagai respons terhadap aritmatika.
Banyak jawaban berkaitan dengan kinerja (well, 32bit lebih cepat dari 8bit). Pada kenyataannya, angka 8bit masih merupakan angka 32bit ke CPU 32bit .... bahkan jika Anda menambahkan dua byte, potongan data yang beroperasi cpu akan menjadi 32bits terlepas ... jadi menambahkan ints tidak akan menjadi "lebih cepat" daripada menambahkan dua byte ... semuanya sama dengan cpu. SEKARANG, menambahkan dua int AKAN lebih cepat daripada menambahkan dua lama pada prosesor 32bit, karena menambahkan dua lama membutuhkan lebih banyak microops karena Anda bekerja dengan angka yang lebih luas daripada kata prosesor.
Saya pikir alasan mendasar untuk menyebabkan byte aritmatika untuk menghasilkan int cukup jelas dan lurus ke depan: 8bits tidak terlalu jauh! : D Dengan 8 bit, Anda memiliki rentang 0-255 yang tidak ditandatangani. Itu tidak banyak ruang untuk bekerja dengan ... kemungkinan bahwa Anda akan mengalami keterbatasan byte SANGAT tinggi ketika menggunakannya dalam aritmatika. Namun, kemungkinan Anda kehabisan bit ketika bekerja dengan int, atau long, atau dobel, dll. Secara signifikan lebih rendah ... cukup rendah sehingga kami jarang sekali menemukan kebutuhan untuk lebih.
Konversi otomatis dari byte ke int adalah logis karena skala byte sangat kecil. Konversi otomatis dari int ke long, float ke double, dll. Tidak logis karena angka-angka tersebut memiliki skala yang signifikan.
sumber
byte - byte
kembaliint
, atau mengapa mereka tidak memberikanshort
...byte + byte
kembaliint
, karena 255 + apa pun lebih besar dari satu byte dapat menampung, tidak masuk akal untuk memiliki byte dikurangi byte lain mengembalikan apa pun selain int dari sudut pandang konsistensi jenis kembali.byte
pengurangan akan mengembalikan abyte
, dan penambahan byte akan mengembalikan ashort
(byte
+byte
akan selalu masuk ke dalam ashort
). Jika itu tentang konsistensi seperti yang Anda katakan, makashort
masih cukup untuk kedua operasi daripadaint
. Jelas ada campuran alasan, tidak semuanya dipikirkan dengan matang. Atau, alasan kinerja yang diberikan di bawah ini mungkin lebih akurat.