Soru byte + byte = int… neden?


Bu C # koduna bakıyorum:

byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'

Yapılan herhangi bir matematik sonucu byte (veya short) türler dolaylı olarak bir tam sayıya geri döker. Çözüm, sonucu bir bayta açık bir şekilde geri göndermelidir:

byte z = (byte)(x + y); // this works

Merak ettiğim şey neden? Mimari mi? Felsefi?

Sahibiz:

  • int + int = int
  • long + long = long
  • float + float = float
  • double + double = double

Yani neden olmasın:

  • byte + byte = byte
  • short + short = short?

Biraz arka plan: "Küçük sayılar" (yani <8) üzerinde uzun bir hesaplama listesi gerçekleştiriyorum ve ara sonuçları büyük bir dizide saklıyorum. Kullanma bayt dizisi (int dizisi yerine) daha hızlı (önbellek isabetlerinden dolayı). Ancak, kod boyunca yayılan geniş baytlar, daha okunamaz hale getiriyor.


332
2018-06-02 19:59


Menşei


stackoverflow.com/questions/927391/... - Timothy Carter
Eric'in bilgisi yok. standart Burada yararlı olurdu - onun bilgisi dizayn dilin neden olmasın Ama evet, Eric'in cevabı oldukça kesin olurdu :) - Jon Skeet
Aşağıdaki çeşitli musilikler, tasarım değerlendirmelerinin makul bir yaklaşımıdır. Daha genel olarak: Baytları "sayı" olarak düşünmüyorum; Onları olabilecek bit kalıpları olarak düşünüyorum. yorumlanır sayılar, karakterler veya renkler veya her neyse. Onlara matematik yapacak ve onları sayı olarak ele alacaksanız, sonucu daha çok bir sayı olarak yorumlanmış bir veri türüne taşımak mantıklıdır. - Eric Lippert
@Eric: Bu byte için çok mantıklı, ama muhtemelen kısa / ushort için çok fazla mantıklı değil. - Jon Skeet
@Eric: byte1 | byte2 onları hiç sayı olarak ele almıyor. Bu onları tam olarak bit kalıpları olarak ele alıyor. Senin bakış açınızı anlıyorum, ama öyle oluyor ki, C # 'de herhangi bir aritmetik yaptığım her defasında, aslında onları sayı olarak değil, bit olarak ele alıyordum ve bu davranış her zaman yolunda. - Roman Starkov


Cevaplar:


Kod snippet'inin üçüncü satırı:

byte z = x + y;

aslında demek

byte z = (int) x + (int) y;

Bu nedenle, bayt üzerinde + işlem yoktur, baytlar ilk olarak tamsayılara dökülür ve iki tam sayı eklenmesinin sonucu bir (32 bit) tamsayıdır.


197
2018-06-02 20:17



Aşağıdaki kodu denedim ama hala çalışmıyor. bayt z = (bayt) x + (bayt) y; - Anonymous
Bunun nedeni bayt için + işleminin olmamasıdır (yukarı bakın). Byte z = (byte) ((int) x + (int) y) 'yi deneyin. - azheglov
Bu en doğru, özlü cevap olmalı. Baytlar arasında eklenecek bir işlenmez, bu nedenle "iki bayt ekleme" nin neden işe yaramadığını açıklamak yerine (asla olmadı), bu açıkça sonucun bir int olduğunu gösterir, çünkü gerçekleşen tek şey 2 in bir ek. - RichardTheKiwi
Diğer bütün cevapları okuyarak başım dönüyor (Bay Jon Skeet'e karşı herhangi bir suç). Bu, başlığın altında neler olup bittiğini doğru olarak tanımlayan en basit cevap olduğunu fark ettim. Teşekkürler! - rayryeng
İşte bu derleyiciye dayalı otomatik tanıtımın ne zaman olduğunu belirlemek için bir program içeren başka bir yerde yazdığım bir cevap intmeydana geliyor: stackoverflow.com/a/43578929/4561887 - Gabriel Staples


"Neden olduğu hiç" açısından, çünkü C # tarafından diğerlerinin söyledikleri gibi, byte, sbyte, kısa veya ushort ile aritmetik için tanımlanan herhangi bir operatör yoktur. Bu cevap hakkında niye ya Bu operatörler tanımlanmadı.

Temelde performans uğruna olduğuna inanıyorum. İşlemciler, 32 bit ile çok hızlı bir şekilde aritmetik yapmak için yerel işlemlere sahiptir. Dönüşümü sonuçtan otomatik olarak bir byte'a yapmak could yapılmalı, ancak aslında bu davranışı istemediğiniz durumda performans cezaları ile sonuçlanacaktır.

ben düşünmek bu, açıklamalı C # standartlarından birinde belirtilmiştir. Looking ...

DÜZENLEME: Rahatsız edici, şimdi ek açıklamalı ECMA C # 2 spec, açıklamalı MS C # 3 özellikleri ve açıklama CLI spec ile baktı ve Yok Onlardan görebildiğim kadarıyla söz et. Ben emin Yukarıda verilen sebebi gördüm, ama nerede olduğunu bilerek uçurum. Özür dileriz, referans hayranları :(


157
2018-06-02 20:08



Bunu söylediğim için üzgünüm, ama bunu en iyi cevap olarak görmüyorum. - VVS
Düştün mü her En iyisi olmasın diye cevap ver. ;) - Jon Skeet
(Sadece açıklığa kavuşturmak için, gerçekten sana bir şey yapmıyorum. Herkesin kendi kısaltması için kendi kriterleri var gibi görünüyor ve sorun değil. Sadece ideal olmayandan ziyade aktif olarak zararlı olduğuna inandığım zaman cevap vermeyi reddediyorum. ) - Jon Skeet
En üstteki "en iyi" cevabı almak için oylamayı bir araç olarak kullanıyorum. Aslında cevabımdaki cevabın ana sebebi olan çok fazla şey söylemediğini buldum. Başka bir neden belki de öznel düşüncem, oy kullanmanız söz konusu olduğunda size büyük bir bonus veriyor ve "daha iyi" yanıtların üstüne çıkıyorsunuz. - VVS
IMO, "en iyi" yanıtı almanın en iyi yolu bunu kabul etmektir. Dürüst olmak gerekirse, bence en bilgilendirici cevap Eric'in sorudaki yorumu ... ama bunun dışında, tasarım perspektifi için (derleyicinin ne yaptığı "perspektifinden farklı olarak) orada düşünmüyorum olduğu "performans" ın çok fazla cevabı. Özellikle, int (int) int = long'u önerdiği için "taşmayı önlüyor" argümanını (17 oy) almıyorum. - Jon Skeet


ben düşünce Bunu daha önce bir yerlerde görmüştüm. itibaren Bu yazı, Eski Yeni Şey:

Bir fantezi dünyasında yaşadığımızı varsayalım   'byte' ile ilgili işlemlerin sonuçlandığı yerler   'bayt'.

byte b = 32;
byte c = 240;
int i = b + c; // what is i?

Bu fantezi dünyasında, ben   16 olur! Niye ya? Çünkü ikisi   + operatörüne işlenenler   bayt, yani "b + c" toplamı olarak hesaplanır   16 nedeniyle sonuçlanan bir bayt   tamsayı taşması. (Ve, not ettiğim gibi   daha önce, tamsayı taşması yeni   güvenlik saldırısı vektörü.)

DÜZENLE: Raymond, esasen, C ve C ++ yaklaşımını esas olarak savunuyor. Yorumlarda, C # 'nun dilin geriye dönük uyumluluk gerekçesiyle aynı yaklaşımı benimsediği gerçeğini savunuyor.


66
2018-06-02 20:04



Tamsayılarla onları eklersek ve taşarsa, otomatik olarak farklı bir veri türü olarak yayınlanmaz, bu nedenle neden bayt ile yapılır? - Ryan
Ints ile taşar. İnt.MaxValue + 1 eklemeyi deneyin, 2147483648 yerine -2147483648 olsun. - David Basarab
@ Longhorn213: Evet, Ryan'ın söylediği şey bu: int math taşabilir, ama int math uzun dönmez. - Michael Petrotta
Kesinlikle. Bunun bir güvenlik önlemi olması gerekiyorsa, çok zayıf bir şekilde uygulanmış bir tanesidir.) - Jon Skeet
@Ryan: "tembel" ilkel matematik kadar temel bir şey için C # dil tasarımcılarına karşı yapmak için oldukça ağır bir yük. Onları herhangi bir şeyle suçlamak istiyorsanız, "C / C ++ ile geriye dönük uyumluluk" yapın. - Michael Petrotta


C #

ECMA-334, eklemenin sadece int + int, uint + uint, long + long ve ulong + ulong'da (ECMA-334 14.7.4) yasal olarak tanımlandığını belirtmektedir. Bu nedenle, bunlar 14.4.2'ye göre dikkate alınacak aday operasyonlardır. Bayttan int, uint, long ve ulong'a dolaylı atımlar olduğu için, tüm ekleme işlevi üyeleri 14.4.2.1 altındaki uygulanabilir işlev üyeleridir. 14.4.2.3'teki kurallara göre en iyi imaları bulmamız gerekiyor:

İnt (T1) 'e göre döküm (C1), (C2)' ye uint (T2) veya ulong (T2) 'ye göre dökümden daha iyidir çünkü:

  • T1 int ve T2 uint veya ulong ise C1 daha iyi dönüşümdür.

İnt (T1) 'a kadar döküm (C1), (C2)' den uzun (T2) 'ye göre daha iyidir.

  • T1'den T2'ye dolaylı bir dönüşüm varsa ve T2'den T1'e hiçbir dolaylı dönüşme yoksa, C1 daha iyi bir dönüşümdür.

Bu nedenle int int int int işlevi kullanılır.

C # belirtiminde çok derin gömülü olduğunu söylemek için çok uzun bir yoldur.

CLI

CLI sadece 6 tipte çalışır (int32, native int, int64, F, O ve &). (ECMA-335 bölüm 3 bölüm 1.5)

Bayt (int8) bu türlerden biri değildir ve otomatik olarak ekleme işleminden önce bir int32'ye zorlanır. (ECMA-335 bölüm 3 bölüm 1.6)


56
2018-06-02 23:00



ECMA'nın yalnızca söz konusu operasyonların bir dili başka kuralların uygulanmasını engellemeyeceğini belirlemesi. VB.NET yararlı bir şekilde izin verecek byte3 = byte1 And byte2 cast olmadan, ancak yardımcı olmayan bir çalışma zamanı istisnası atarsa int1 = byte1 + byte2 255'in üzerinde bir değer verir. Herhangi bir dilin izin verip vermeyeceğini bilmiyorum byte3 = byte1+byte2 ve 255'i aştığında bir istisna atmak, ancak bir istisna atmamak int1 = byte1+byte2 256-510 aralığında bir değer verir. - supercat


Bazı verimsizlik baytları ekleyerek ve sonucu bir bayta keserek gösteren yanıtlar yanlıştır. x86 işlemcileri, 8 bitlik miktarlarda tamsayı işlemi için özel olarak tasarlanmış talimatlara sahiptir.

Aslında, x86 / 64 işlemcileri için 32 bit veya 16 bit işlemlerin gerçekleştirilmesi, kodu çözülmesi gereken işlenen önek baytından dolayı 64 bit veya 8 bit işlemlerden daha az verimlidir. 32 bit makinelerde, 16 bit işlemlerin yapılması aynı cezayı gerektirir, ancak 8 bitlik işlemler için hala özel opcodes vardır.

Birçok RISC mimarisinde benzer yerel sözcük / bayt verimli yönergeleri bulunur. Genellikle bir mağaza-ve-dönüştürmek-imzalanmış-değeri-biraz-bit uzunluğu olmayanlar.

Diğer bir deyişle, bu karar, bayt tipinin ne olduğunun, donanımın temel verimsizliklerinden kaynaklanmadığı algısına dayandırılmalıdır.


23
2018-06-02 21:30



Güzel. Bunu bilmiyordum. - PeterAllenWebb
+ 1; Şimdiye kadar hiç değişmediğim her seferinde bu algı yanlış olsaydı ve C # 'da iki bayt olur muydu? - Roman Starkov
Sonucun kesilmesi için herhangi bir performans maliyeti olmamalıdır. X86 derlemesinde, kayıttan bir bayt veya kayıttan dört bayt kopyalama arasındaki fark sadece. - Jonathan Allen
@JonathanAllen Tam olarak. Tek fark, ironik olarak, bir genişletme dönüştürme. şimdiki Tasarım, genişletme talimatını (imzalanmış uzatılmış veya imzasız uzatılmış) yürütmek için bir performans cezası gerektirir. - reirab
"byte türünün ne olduğu algısı"- Bu davranışını açıklayabilir byte (ve char), ancak değil short hangi semantik olarak açıkça bir sayıdır. - smls


Bir zamanlar Jon Skeet'ten birşeyler okuduğumu hatırlıyorum (şimdi bulamıyorum, bakmaya devam edeceğim), byte'ın aslında operatöre aşırı yüklenmediği hakkında. Aslında, örneğinizde olduğu gibi iki bayt eklerken, her bayt aslında bir int'ye dolaylı olarak dönüştürülür. Bunun sonucu açıkça bir int. Şimdi NEDEN bu şekilde tasarlandıysa, Jon Skeet'in kendisini yazmasını bekleyeceğim :)

DÜZENLE: Bulundu! Bu konu hakkında harika bilgi İşte.


13
2018-06-02 20:06



Güzel bul - bu iyi bir referans - Michael Haren


Bunun nedeni taşma ve taşınmasıdır.

İki 8 bit sayı eklerseniz, 9. bit'e taşabilirler.

Örnek:

  1111 1111
+ 0000 0001
-----------
1 0000 0000

Emin değilim, ama sanırım ints, longs, vedoubles Daha fazla alan verilir çünkü oldukları kadar büyüktürler. Ayrıca, dahili veri yolunun genişliği 4 bayt veya 32 bit (64 bit daha yaygın hale gelmektedir) genişliğinden dolayı, bilgisayarların işlem yapması için daha verimli olan 4 katlarıdır. Bayt ve kısa, biraz daha verimsiz, ancak yerden tasarruf edebilirler.


6
2018-06-02 20:03



Ancak daha büyük veri tipleri aynı davranışı takip etmemektedir. - Inisheer
Taşma sorunları bir yana. Eğer mantığınızı alıp dilde uyguladıysanız, tüm veri türleri aritmetik eklendikten sonra daha büyük bir veri türü döndürecektir, ki bu durum kesinlikle OLMAZ. int + int = int, uzun + uzun = uzun. Bence soru tutarsızlık konusunda. - Joseph
Kabul ettim, yazımı yeniden okudum ve düzenledim. - samoz
Bu benim ilk düşüncemdi ama neden int + int = long değil? Bu yüzden "olası taşma" tartışmasını almıyorum ... henüz <grin>. - Robert Cartaino♦
Oh, ve "olası taşma" argeument hakkında, neden byte + byte = kısa değil? - Robert Cartaino♦


C # dil spec 1.6.7.5 7.2.6.2 İkili nümerik promosyonlardan, hem işlenenleri, int diğer birkaç kategoriye sığmıyorsa int'ye dönüştürür. Tahminimce, bir operatör olarak bayt almak için + operatörüne aşırı yüklenmediler, ancak normal olarak davranmasını istiyorlar, böylece sadece int veri tipini kullanıyorlar.

C # dil Özelliği


5
2018-06-02 20:13





Şüphem C # aslında aslında operator+ üzerinde tanımlanmış int (bir int Eğer bir checked blok) ve her ikisini de bytes/shorts için ints. Bu yüzden davranış tutarsız görünüyor.


4
2018-06-02 20:05



Her iki baytı da yığının üstüne basar, sonra "add" komutunu çağırır. IL'de, iki değeri "yiyin" ve bir int ile değiştirir. - Jonathan Allen


Bu muhtemelen dil tasarımcılarının uyguladığı bir karardı. Sonuçta, int bir Int32, 32 bit imzalı bir tamsayıdır. Int'ten küçük bir tam sayı işlemi yaptığınızda, herhangi bir 32 bit CPU tarafından 32 bit imzalı int'ye dönüştürülür. Bu, küçük tam sayıların taşma ihtimali ile birleştiğinde, muhtemelen anlaşmayı imzaladı. Aşırı / düşük akışı sürekli kontrol etme işinden kurtarır ve bayt üzerindeki bir ifadenin son sonucu, bazı ara aşamalarda aralığın dışında kalmasına rağmen, doğru bir şekilde elde edersiniz. sonuç.

Başka bir düşünce: Bu tipler üzerinde aşırı / düşük akış, simüle edilmek zorundaydı çünkü en olası hedef CPU'larda doğal olarak oluşmayacaktı. Neden rahatsız oluyorsun?


3
2018-06-02 20:06