byte + byte = int почему?

Глядя на этот код C #:

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

Результат любой математики, выполняемой с типами byte (или short), неявно приводится обратно к целому числу. Решение состоит в явном преобразовании результата в байт:

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

Мне интересно, почему? Это архитектурное? Философский?

У нас есть:

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

Так почему бы и нет:

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

Немного предыстории: я выполняю длинный список вычислений с «маленькими числами» (т.е. ‹8) и сохраняю промежуточные результаты в большом массиве. Использование байтового массива (вместо массива int) быстрее (из-за попаданий в кеш). Но обширное преобразование байтов в код делает его гораздо более нечитаемым.


person Robert Cartaino    schedule 02.06.2009    source источник
comment
stackoverflow.com/questions/927391/   -  person Timothy Carter    schedule 03.06.2009
comment
Я предполагаю, что это как-то связано с тем, насколько легко было бы переполнить байт несколькими добавками. Однако я бы подумал, что это оставят программисту, а не архитектурно ограничивают таким образом.   -  person Matt Grande    schedule 03.06.2009
comment
Вы уверены, что не оптимизируете это на микроуровне? IIRC, на 32-битной машине байт будет выровнен по 32-битным границам для оптимизированного доступа, то есть один байт фактически будет использовать 4 байта в памяти.   -  person Dirk Vollmar    schedule 03.06.2009
comment
Эрик Липперт, где ты, когда ты нам нужен ?? Энциклопедические знания стандарта C # спешат на помощь!   -  person Adam V    schedule 03.06.2009
comment
Здесь может оказаться полезным не знание Эриком стандарта - это его знание дизайна языка; что не почему. Но да, ответ Эрика был бы довольно определенным :)   -  person Jon Skeet    schedule 03.06.2009
comment
Приведенные ниже различные размышления являются разумным приближением к соображениям дизайна. В более общем плане: я не считаю байты числами; Я думаю о них как о паттернах битов, которые можно интерпретировать как числа, символы, цвета или что-то еще. Если вы собираетесь проводить с ними математические вычисления и рассматривать их как числа, тогда имеет смысл переместить результат в тип данных, который обычно интерпретируется как число.   -  person Eric Lippert    schedule 03.06.2009
comment
@Eric: Это имеет смысл для байта, но, вероятно, не так много смысла для short / ushort.   -  person Jon Skeet    schedule 03.06.2009
comment
Эрик - ваш комментарий действительно лучший ответ на этот вопрос. Хотите дать на него ответ?   -  person Michael Petrotta    schedule 03.06.2009
comment
Что сказал Майкл. Сделайте ответ, и я проголосую за него.   -  person David Thornley    schedule 03.06.2009
comment
Кстати, если ваши числа всегда меньше 8, вы можете хранить два из них на байт и снова вдвое сократить количество промахов в кэше.   -  person Crashworks    schedule 19.06.2009
comment
@Eric: byte1 | byte2 вовсе не считает их числами. Это трактует их именно как образцы битов. Я понимаю вашу точку зрения, но так получилось, что каждый раз, когда я выполнял арифметические операции с байтами в C #, я фактически рассматривал их как биты, а не числа, и такое поведение всегда мешает.   -  person Roman Starkov    schedule 28.12.2009
comment
@Crashworks, хотя байты кажутся всего 8-битными, С # часто округляет их на границе стека 4 или 8 байтов при использовании в качестве локальных переменных. То же самое и с использованием регистров - хотя regs на 86 являются 32-битными, сгенерированный asm из msil не собирается упаковывать 4-байтовые переменные в один регистр. будет много или загрузка и сохранение, как и для 32-битных значений.   -  person mP.    schedule 21.12.2010
comment
Возможный дубликат блюз целочисленного суммирования, короткий + = короткая задача   -  person GSerg    schedule 14.04.2016
comment
@GSerg Этот вопрос был первым и набрал много голосов по всем направлениям. У вас неправильное направление закрытия.   -  person Robert Cartaino    schedule 14.04.2016
comment
@RobertCartaino Да, этот вопрос старше, однако другой содержит гораздо более ценный ответ Эрика Липперта. Я бы назвал его каноническим ответом.   -  person GSerg    schedule 14.04.2016
comment
@GSerg Тогда вопросы следовало объединить, но это действительно опасное утверждение, судить о вопросе в целом по тому, кто на него ответил, а не по проверке содержания. Эрик Липперт здесь также прокомментировал проблему, и у меня есть Джон Скит. Моя пара побила ваш единственный в своем роде; не уверен, что мы должны играть в эту игру. Гаст, будет довольно забавно, что я писал о достоинствах этого конкретного вопроса (сотни раз) только для того, чтобы обнаружить, что он был закрыт сообществом. Классический.   -  person Robert Cartaino    schedule 15.04.2016
comment
@RobertCartaino Затем сделайте последнее голосование, снова откройте и оставьте ссылку на другого. Я не закрываю все вопросы, кроме одного, я просто хочу, чтобы все они были связаны.   -  person GSerg    schedule 15.04.2016
comment
Вот ответ, который я написал в другом месте, который содержит программу для определения того, когда происходит это управляемое компилятором автоматическое продвижение до int (по крайней мере, на C или C ++): stackoverflow.com/a/43578929/4561887   -  person Gabriel Staples    schedule 28.04.2017
comment
@EricLippert (да, я знаю, что прошло много лет), который не объясняет, почему такая же проблема существует для выполнения логических операций с bytes.   -  person The Photon    schedule 11.07.2017


Ответы (16)


Третья строка вашего фрагмента кода:

byte z = x + y;

на самом деле означает

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

Таким образом, для байтов нет операции +, байты сначала преобразуются в целые числа, а результатом сложения двух целых чисел является (32-битное) целое число.

person azheglov    schedule 02.06.2009
comment
Я пробовал код ниже, но он все еще не работает. байт z = (байт) x + (байт) y; - person Anonymous; 04.06.2009
comment
это потому, что для байтов нет операции + (см. выше). Попробуйте byte z = (byte) ((int) x + (int) y) - person azheglov; 05.06.2009
comment
Это должен быть самый правильный и краткий ответ. Нет операнда для добавления между байтами, поэтому вместо объяснения того, почему добавление двух байтов работает или нет (этого никогда не было), это ясно показывает, почему результатом является int, потому что единственное, что произошло, это добавление 2 целых. - person RichardTheKiwi; 04.04.2011
comment
У меня закружилась голова, читая все остальные ответы (без обид на мистера Джона Скита). Я обнаружил, что это самый простой ответ, который правильно описывает то, что происходит под капотом. Спасибо! - person rayryeng; 07.01.2015
comment
Вот ответ, который я написал в другом месте, который содержит программу для определения того, когда происходит это управляемое компилятором автоматическое продвижение до int: stackoverflow.com/a/ 43578929/4561887 - person Gabriel Staples; 25.04.2017
comment
ОП спрашивает, зачем ему кастовать. Он не приводит к int / long / uint ..., но почему только для байта? а также _1 _ / _ 2_. Вы складываете два short и получаете тип int? Тогда почему не два int становятся long? Это больше похоже на непоследовательное дизайнерское решение. - person joe; 18.04.2021

С точки зрения «почему это вообще происходит», это потому, что C # не определяет никаких операторов для арифметики с байтами, sbyte, short или ushort, как говорили другие. Этот ответ о том, почему не определены эти операторы.

Я считаю, что это в основном ради производительности. Процессоры имеют встроенные операции, позволяющие очень быстро выполнять 32-битные арифметические операции. Выполнение обратного преобразования результата в байт автоматически может быть выполнено, но приведет к снижению производительности в случае, если вы на самом деле не хотите такого поведения.

Я думаю, что это упоминается в одном из аннотированных стандартов C #. Смотрящий...

РЕДАКТИРОВАТЬ: досадно, что теперь я просмотрел аннотированную спецификацию ECMA C # 2, аннотированную спецификацию MS C # 3 и спецификацию аннотации CLI, и ни один из них не упоминает об этом, насколько я могу видеть. Я уверен, что я видел причину, указанную выше, но меня взорвет, если я знаю, где. Извинения, ссылочные фанаты :(

person Jon Skeet    schedule 02.06.2009
comment
Мне жаль это говорить, но я считаю, что это не лучший ответ. - person VVS; 03.06.2009
comment
Вы проголосовали против за каждый ответ, который, по вашему мнению, не является лучшим? ;) - person Jon Skeet; 03.06.2009
comment
(Чтобы прояснить, я на самом деле не имею к вам никакого отношения. Кажется, у каждого есть свои собственные критерии для голосования против, и это нормально. Я голосую против ответа только в том случае, если считаю, что он вреден, а не просто не идеален. ) - person Jon Skeet; 03.06.2009
comment
Я использую голосование как инструмент, чтобы получить лучший ответ наверху. На самом деле я обнаружил, что вы вообще мало что сказали в своем ответе, что было основной причиной моего отрицательного голоса. Другой причиной может быть мое субъективное ощущение, что ваша репутация дает вам большой бонус, когда дело доходит до голосования, и вы получаете лучшие ответы. - person VVS; 03.06.2009
comment
ИМО, лучший способ получить лучший ответ - это проголосовать за него. Честно говоря, я думаю, что наиболее информативным ответом здесь является комментарий Эрика в вопросе ... но кроме этого, с точки зрения дизайна (в отличие от перспективы того, что делает компилятор), я не думаю, что есть много ответов, помимо производительности. В частности, я действительно не покупаю аргумент, предотвращающий переполнение (17 голосов), поскольку это предполагает int + int = long. - person Jon Skeet; 03.06.2009
comment
Если вы имеете в виду ответ Майкла Петротты: главный аргумент - это сообщение в блоге ИМО Раймонда Чена и его отличный пример «что если». ИМО никто не рекомендовал бы, чтобы 32 + 240 было 16 только потому, что эти числа хранятся в байтах. Возвращаясь к вашему второму комментарию: вы правы. Поскольку ваш ответ неплох или неправильный, у меня нет причин понижать его. - person VVS; 03.06.2009
comment
Я бы сказал, что для симметрии 32 + 240, превращающееся в 16, так же логично, как int.MaxValue + 1, являющееся int.MinValue (по модулю комментарий Эрика о том, что байт на самом деле не столько число, сколько набор битов). Приятно, что у нас есть концепция проверенного контекста в C # ... - person Jon Skeet; 03.06.2009
comment
Некоторым людям этот скучный ответ не нравится, потому что он слишком практичен. Они хотят чего-то более концептуального. Мне этот практический ответ кажется гораздо более правдоподобным: когда вы разрабатываете спецификацию, вам также необходимо принимать во внимание практические соображения. Тип int предназначен для добавления с использованием процессора, а байт предназначен для хранения данных. Когда вы делаете добавление, вы используете тип данных, оптимизированный для добавления. - person charles; 13.07.2010
comment
@Jon не только сделал это (возможно), но и пометил здесь все комментарии. - person ; 09.11.2010
comment
@JonSkeet: кстати, у вас есть ссылки на аннотированные спецификации C #? Или у вас есть бумажные копии? - person ; 05.04.2011
comment
@Will: У меня есть бумажные копии - стоит их получить: amazon .com / dp / 0321741765 - person Jon Skeet; 05.04.2011
comment
Я не понимаю, почему некоторые отвергают этот ответ; прежде всего, чтобы указать, что суть в том, почему эти операторы не определены, значительно полезнее, чем просто сказать они не являются. - person Ken Kin; 11.05.2013
comment
Вернемся к теме ... я предполагаю, что это потому, что байт чаще используется для представления ровно 8 бит информации, тогда как int (и long и т. Д.) Гораздо чаще используется для представления целого числа. Целое число обычно больше, чем сумма его частей, тогда как байт обычно предназначен для битовых полей, необработанного доступа к памяти и т.д. чтобы быть более распространенным числовым типом. - person nathanchere; 28.04.2014
comment
@JonSkeet - единственное место, где я видел упоминания, похожие на то, что вы сказали, - это вкратце C #: 8- и 16-битные интегралы не имеют собственных арифметических операторов, и компилятор неявно преобразовал их в более крупный тип (int32) по мере необходимости. - person stt106; 23.05.2015
comment
Хм ... Разве большинство архитектур 32-битных процессоров не поддерживают 8- и 16-битные арифметические операции? Я точно знаю, что x86 делает, потому что сам много раз делал это на сборке x86. Кроме того, большинство чипов x86 в наши дни на самом деле 64-битные, поэтому, согласно аргументу этого ответа, все типы должны автоматически расширяться до 64 бит, прежде чем выполнять математику, чего они не делают. - person reirab; 26.06.2015
comment
@reirab: Честно говоря, не знаю. Я уверен, что где-то убедительно читал этот аргумент об эффективности, но сейчас не могу вспомнить, где :( - person Jon Skeet; 26.06.2015
comment
@JonSkeet: причина, по которой вы описываете, в точности относится к архитектуре ARM. Процессоры на базе ARMv4 могут эффективно загружать и хранить 8-, 16- и 32-битные данные. Однако большинство операций обработки данных ARM - только 32-разрядные. По этой причине вы должны использовать 32-битный тип данных, int или long, для локальных переменных везде, где это возможно. Избегайте использования char и short в качестве типов локальных переменных, даже если вы манипулируете 8- или 16-битным значением - ссылка, стр. 107 - person BlueStrat; 13.07.2016
comment
Как-то это работает. байт x = 1; байт y = 2; х + = у; Console.WriteLine (x); - person Kevin Muhuri; 11.12.2020
comment
@KevinMuhuri: Да, потому что += - это составной оператор присваивания, в котором есть неявное приведение. - person Jon Skeet; 11.12.2020

Я подумал, что видел это где-то раньше. Из этой статьи, The Old New Thing:

Предположим, мы живем в фантастическом мире, где операции с байтом приводят к байту.

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

В этом фантастическом мире значение i было бы 16! Почему? Поскольку два операнда оператора + являются байтами, сумма «b + c» вычисляется как байт, что дает 16 из-за целочисленного переполнения. (И, как я отмечал ранее, целочисленное переполнение - это новый вектор атаки на безопасность.)

РЕДАКТИРОВАТЬ: Раймонд, по сути, защищает исходный подход C и C ++. В комментариях он защищает тот факт, что C # использует тот же подход, исходя из обратной совместимости языка.

person Michael Petrotta    schedule 02.06.2009
comment
С целыми числами, если мы добавляем их, и оно переполняется, оно не автоматически переводит его как другой тип данных, но зачем это делать с байтом? - person Ryan; 03.06.2009
comment
@Ryan: Полагаю, Microsoft предвидела больше проблем с байтовой арифметикой, чем с int math, из-за меньшего динамического диапазона байтов. - person Michael Petrotta; 03.06.2009
comment
С ints это переполняется. Попробуйте добавить int.MaxValue + 1, и вы получите -2147483648 вместо 2147483648. - person David Basarab; 03.06.2009
comment
@ Longhorn213: Ага, вот что говорит Райан: int math может переполняться, но int math не возвращает long. - person Michael Petrotta; 03.06.2009
comment
Точно. Если это должно быть мерой безопасности, она очень плохо реализована;) - person Jon Skeet; 03.06.2009
comment
Я не уверен, что Раймонд (хоть раз) должен считаться авторитетом в этом вопросе. Прочтите комментарии к записи в блоге. Он защищает это в C # в основном потому, что C ++ делает это таким образом blogs.msdn.com/oldnewthing/archive/2004/03/10/87247.aspx#87811. - person Aardvark; 03.06.2009
comment
Я бы сказал, это просто лень. Смотрите мой ответ ниже. Все разработчики ленивы, просто не хотели лишний раз перегружать оператора. - person Ryan; 03.06.2009
comment
@Ryan: ленивость - это довольно серьезное обвинение в адрес разработчиков языка C # за такую ​​простую вещь, как примитивная математика. Если вы хотите обвинить их в чем-либо, сделайте излишнюю обратную совместимость с C / C ++. - person Michael Petrotta; 03.06.2009
comment
java делает то же самое. независимо от источника идеи, она, кажется, довольно хорошо закрепилась у разработчиков языка. - person JustJeff; 03.06.2009
comment
Чрезмерная обратная совместимость с C / C ++ может быть лучшим способом выразить это. Но затем мы можем расширить вопрос о том, почему C был разработан таким образом, и начать заново. - person Ryan; 03.06.2009
comment
C был разработан для эффективной работы на очень примитивных (по нашим стандартам) процессорах, у которых, вероятно, не было инструкций для целочисленной арифметики для чего-либо, кроме стандартного размера слова. Если вы посмотрите на язык программирования C, он упоминает, что int обычно является собственным размером слова процессора, на который нацелен компилятор. - person JoelFan; 03.02.2010
comment
JustJeff: Я могу назвать больше языков, которые молча переходят в bignums, чем языков, которые молча переполняются. Кажется, что он в меньшей степени укоренился в языковых разработчиках, чем даже синтаксис в стиле Си. - person Ken; 08.11.2010
comment
VB.net работает как мир фэнтези. Я бы не сказал, что это хорошо. - person supercat; 01.03.2012
comment
@JonSkeet Возможно ли, что это не проблема безопасности / безопасности, а тот факт, что, возможно, на уровне процессора невозможно обнаружить короткое + короткое переполнение. Т.е. если фактическая инструкция процессора .NET отображает операцию на 32-битную и checked арифметическую, процессор не будет отмечать переполнение, потому что AFAitK это не было 32-битным переполнением, даже если это было бы переполнением с точки зрения краткости. Таким образом, помимо проблем с производительностью, которые вы отметили в своем ответе, проверенная арифметика пострадает больше, потому что .NET не может полагаться на процессор, обнаруживающий переполнения (таким образом, проверяя вручную) - person AaronLS; 23.11.2013
comment
Я говорю только о поверхностной памяти давнего класса проектирования процессоров: / Я хочу сказать, что, возможно, переполнение действительно является проблемой для этого проектного решения, но, вероятно, не по той причине, которую подразумевал этот ответчик. - person AaronLS; 23.11.2013
comment
@AaronLS: Проверенная арифметика всегда может быть эмулирована - я даже не знаю, как это выглядит на уровне сборки для целых чисел, если честно, но вы, безусловно, можете добавить проверки. - person Jon Skeet; 23.11.2013
comment
Это рассуждение ошибочно ... Попробуйте заменить int на long и byte на _4 _..., например long v = int.MaxValue + 1; Или, возможно, старая проблема _6 _; ... - person xanatos; 20.07.2015

C#

ECMA-334 утверждает, что добавление разрешено только для int + int, uint + uint, long + long и ulong + ulong (ECMA-334 14.7.4). Таким образом, это операции-кандидаты, которые следует учитывать в отношении 14.4.2. Поскольку существуют неявные преобразования байтов в int, uint, long и ulong, все добавляемые функциональные члены являются применимыми функциональными членами согласно 14.4.2.1. Мы должны найти наилучшее неявное приведение по правилам в 14.4.2.3:

Приведение (C1) к int (T1) лучше, чем приведение (C2) к uint (T2) или ulong (T2), потому что:

  • Если T1 - это int, а T2 - uint или ulong, C1 - лучшее преобразование.

Приведение (C1) к int (T1) лучше, чем приведение (C2) к long (T2), потому что существует неявное приведение от int к long:

  • Если существует неявное преобразование из T1 в T2, и не существует неявного преобразования из T2 в T1, лучшим преобразованием является C1.

Следовательно, используется функция int + int, которая возвращает int.

Это очень далеко от того, чтобы сказать, что это глубоко укоренилось в спецификации C #.

CLI

CLI работает только с 6 типами (int32, native int, int64, F, O и &). (ECMA-335, раздел 3, раздел 1.5)

Byte (int8) не является одним из этих типов и автоматически приводится к int32 перед добавлением. (ECMA-335, раздел 3, раздел 1.6)

person Alun Harford    schedule 02.06.2009
comment
То, что ECMA определяет только эти конкретные операции, не мешает языку реализовывать другие правила. VB.NET услужливо разрешит byte3 = byte1 And byte2 без преобразования, но бесполезно вызовет исключение времени выполнения, если int1 = byte1 + byte2 даст значение больше 255. Я не знаю, разрешат ли какие-либо языки byte3 = byte1+byte2 и вызовут исключение, когда оно превышает 255, но не вызовет исключение, если int1 = byte1+byte2 дает значение в диапазоне 256-510. - person supercat; 07.07.2014

Ответы, указывающие на некоторую неэффективность добавления байтов и усечения результата до байта, неверны. Процессоры x86 имеют инструкции, специально разработанные для целочисленных операций с 8-битными величинами.

Фактически, для процессоров x86 / 64 выполнение 32-битных или 16-битных операций менее эффективно, чем 64-битные или 8-битные операции из-за байта префикса операнда, который необходимо декодировать. На 32-битных машинах выполнение 16-битных операций влечет за собой такие же штрафы, но все еще есть специальные коды операций для 8-битных операций.

Многие архитектуры RISC имеют аналогичные встроенные команды, эффективные для слов и байтов. Те, которые обычно не имеют значения длины в битах для хранения и преобразования в знаковое значение.

Другими словами, это решение должно было быть основано на восприятии того, для чего предназначен байтовый тип, а не из-за лежащей в основе неэффективности оборудования.

person Christopher    schedule 02.06.2009
comment
+1; если бы только это восприятие не было неправильным каждый раз, когда я когда-либо сдвигал и использовал два байта в C # с помощью ИЛИ ... - person Roman Starkov; 28.12.2009
comment
Усечение результата не должно приводить к снижению производительности. В сборке x86 это просто разница между копированием одного байта из регистра или четырех байтов из регистра. - person Jonathan Allen; 08.08.2010
comment
@JonathanAllen Совершенно верно. Единственная разница, по иронии судьбы, заключается в выполнении преобразования расширения. Текущая конструкция приводит к снижению производительности при выполнении инструкции расширения (расширение со знаком или расширение без знака). - person reirab; 26.06.2015
comment
восприятие типа байта - это может объяснить такое поведение для bytechar), но не для short, который семантически является числом. - person smls; 19.04.2018

Я помню, как однажды читал что-то от Джона Скита (сейчас не могу найти, буду искать) о том, как байт на самом деле не перегружает оператор +. Фактически, при добавлении двух байтов, как в вашем примере, каждый байт фактически неявно преобразуется в int. Результатом этого, очевидно, является тип int. Теперь что касается того, ПОЧЕМУ это было разработано таким образом, я подожду, пока сам Джон Скит опубликует сообщение :)

РЕДАКТИРОВАТЬ: Нашел! Подробная информация по этой теме здесь.

person BFree    schedule 02.06.2009

Это из-за переполнения и уноса.

Если вы сложите два 8-битных числа, они могут вылиться в 9-й бит.

Пример:

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

Я не знаю наверняка, но предполагаю, что ints, longs иdoubles имеют больше места, потому что они и так довольно большие. Кроме того, они кратны 4, что более эффективно для компьютеров, так как ширина внутренней шины данных составляет 4 байта или 32 бита (сейчас 64 бита становятся все более распространенными). Byte и short немного менее эффективны, но они могут сэкономить место.

person samoz    schedule 02.06.2009
comment
Но более крупные типы данных не следуют такому же поведению. - person Inisheer; 03.06.2009
comment
Вопросы переполнения - в стороне. Если бы вы взяли свою логику и применили ее к языку, тогда все типы данных вернули бы больший тип данных после арифметики сложения, что, безусловно, НЕ имеет места. int + int = int, длинный + длинный = длинный. Думаю, вопрос в непоследовательности. - person Joseph; 03.06.2009
comment
Это была моя первая мысль, но почему тогда не int + int = long? Так что я не верю аргументам о возможном переполнении ... пока ‹grin›. - person Robert Cartaino; 03.06.2009
comment
Да, и насчет возможного аргумента переполнения, почему бы не byte + byte = short? - person Robert Cartaino; 03.06.2009
comment
A) Почему это работает так, как работает с учетом правил C #? Смотрите мой ответ ниже. Б) Почему он был разработан таким, какой он есть? Вероятно, это просто соображения юзабилити, основанные на субъективных суждениях о том, как большинство людей склонны использовать целые числа и байты. - person mqp; 03.06.2009
comment
@Joseph: возникнет ли какая-либо реальная проблема со спецификацией, которая требует, чтобы во всех случаях, когда целочисленное выражение, которое не включает непостоянные сдвиги вправо, могло быть оценено со всеми промежуточными результатами, соответствующими самому большому целочисленному типу со знаком, выражения должны дают тот же результат, что и при оценке с использованием таких типов? Компиляторам не должно быть сложно определить максимально возможные типы, необходимые на каждом этапе (например, если целые числа указаны для переноса, int1=int2+int3; может использовать 32 бита, но int1=(int2+int3)/2; не должен, если он не может сохранить 'перенос'.) - person supercat; 10.12.2012
comment
@Joseph: Тот факт, что longVar &= ~0x80000000 работает совсем не так, как longVar &= 0x40000000 или longVar &= 0x100000000, представляет для меня значительный недостаток в спецификации. Кроме того, если язык будет кричать при присвоении больших чисел меньшим переменным, он должен рассматривать результат x & y, где оба типа подписаны или оба без знака, как входной тип small; если один подписан, а другой нет, результатом должен быть тип без знака. - person supercat; 10.12.2012

Из спецификации языка C # 1.6.7.5 7.2.6.2 Двоичные числовые предложения он преобразует оба операнда в int, если он не может поместиться в несколько других категорий. Я предполагаю, что они не перегружали оператор +, чтобы он принимал байт в качестве параметра, но хотят, чтобы он работал нормально, поэтому они просто используют тип данных int.

Спецификация языка C #

person Ryan    schedule 02.06.2009

Я подозреваю, что C # на самом деле вызывает operator+, определенный в int (который возвращает int, если вы не находитесь в блоке checked), и неявно преобразует оба ваших _5 _ / _ 6_ в ints. Вот почему поведение кажется непоследовательным.

person mqp    schedule 02.06.2009
comment
Он помещает оба байта в стек, а затем вызывает команду добавления. В IL добавление съедает два значения и заменяет их на int. - person Jonathan Allen; 08.08.2010

Вероятно, это было практическое решение со стороны разработчиков языка. В конце концов, int - это Int32, 32-битное целое число со знаком. Всякий раз, когда вы выполняете целочисленную операцию с типом, меньшим, чем int, он в любом случае будет преобразован в 32-битный подписанный int почти любым 32-битным процессором. Это, в сочетании с вероятностью переполнения небольших целых чисел, вероятно, заключило сделку. Это избавляет вас от рутинной работы по постоянной проверке переполнения / недостаточного потока, и когда конечный результат выражения в байтах будет в диапазоне, несмотря на то, что на каком-то промежуточном этапе он будет вне диапазона, вы получите правильный результат.

Другая мысль: необходимо смоделировать переполнение / недостаточный поток для этих типов, поскольку это не произойдет естественным образом на наиболее вероятных целевых процессорах. Зачем беспокоиться?

person PeterAllenWebb    schedule 02.06.2009

По большей части это мой ответ, относящийся к этой теме, который сначала был отправлен на аналогичный вопрос здесь.

По умолчанию все операции с целыми числами меньше Int32 перед вычислением округляются до 32 бит. Причина, по которой результатом является Int32, заключается в том, чтобы просто оставить его таким, каким он был после расчета. Если вы проверите арифметические коды операций MSIL, единственными целочисленными типами, с которыми они работают, являются Int32 и Int64. Это «по замыслу».

Если вы хотите вернуть результат в формате Int16, не имеет значения, выполняете ли вы приведение кода или компилятор (гипотетически) испускает преобразование «под капотом».

Например, чтобы выполнить арифметику Int16:

short a = 2, b = 3;

short c = (short) (a + b);

Два числа расширятся до 32 бит, будут добавлены, а затем снова усечены до 16 бит, как и предполагала MS.

Преимущество использования коротких (или байтовых) значений заключается в первую очередь в хранении в случаях, когда у вас есть большие объемы данных (графические данные, потоковая передача и т. Д.).

person Kenan E. K.    schedule 05.07.2009

Для байтов сложение не определено. Таким образом, они приводятся к типу int для добавления. Это верно для большинства математических операций и байтов. (обратите внимание, что это было так в старых языках, я предполагаю, что это верно и сегодня).

person Jim C    schedule 02.06.2009

Я думаю, что это дизайнерское решение о том, какая операция была более распространенной ... Если byte + byte = byte, может быть, гораздо больше людей будут беспокоиться о необходимости приводить к int, когда в результате требуется int.

person fortran    schedule 02.06.2009
comment
Меня на этот раз беспокоит другое :) Мне всегда нужен байтовый результат, поэтому мне всегда нужно приводить. - person Roman Starkov; 28.12.2009
comment
За исключением того, что вам не нужно приводить к int. Приведение неявное. Только обратный путь очевиден. - person Niki; 15.03.2010
comment
@nikie Я думаю, вы не поняли моего ответа. Если добавление двух байтов приведет к получению байта, чтобы предотвратить переполнение, кто-то должен был бы преобразовать операнды (не результат) в int перед добавлением. - person fortran; 15.03.2010

Из кода .NET Framework:

// bytes
private static object AddByte(byte Left, byte Right)
{
    short num = (short) (Left + Right);
    if (num > 0xff)
    {
        return num;
    }
    return (byte) num;
}

// shorts (int16)
private static object AddInt16(short Left, short Right)
{
    int num = Left + Right;
    if ((num <= 0x7fff) && (num >= -32768))
    {
        return (short) num;
    }
    return num;
}

Упростите с помощью .NET 3.5 и более поздних версий:

public static class Extensions 
{
    public static byte Add(this byte a, byte b)
    {
        return (byte)(a + b);
    }
}

теперь вы можете:

byte a = 1, b = 2, c;
c = a.Add(b);

person serhio    schedule 01.02.2010

Я тестировал производительность между байтом и int.
Со значениями int:

class Program
{
    private int a,b,c,d,e,f;

    public Program()
    {
        a = 1;
        b = 2;
        c = (a + b);
        d = (a - b);
        e = (b / a);
        f = (c * b);
    }

    static void Main(string[] args)
    {
        int max = 10000000;
        DateTime start = DateTime.Now;
        Program[] tab = new Program[max];

        for (int i = 0; i < max; i++)
        {
            tab[i] = new Program();
        }
        DateTime stop = DateTime.Now;

        Debug.WriteLine(stop.Subtract(start).TotalSeconds);
    }
}

Со значениями байтов:

class Program
{
    private byte a,b,c,d,e,f;

    public Program()
    {
        a = 1;
        b = 2;
        c = (byte)(a + b);
        d = (byte)(a - b);
        e = (byte)(b / a);
        f = (byte)(c * b);
    }

    static void Main(string[] args)
    {
        int max = 10000000;
        DateTime start = DateTime.Now;
        Program[] tab = new Program[max];

        for (int i = 0; i < max; i++)
        {
            tab[i] = new Program();
        }
        DateTime stop = DateTime.Now;

        Debug.WriteLine(stop.Subtract(start).TotalSeconds);
    }
}

Вот результат:
байт: 3.57s 157mo, 3.71s 171mo, 3.74s 168mo с CPU ~ = 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo с CPU ~ = 27%
Заключение:
байт использует больше ЦП, но требует меньше памяти и быстрее (возможно, потому, что нужно выделить меньше байтов)

person puipuix    schedule 01.11.2018

В дополнение ко всем другим замечательным комментариям, я подумал, что добавлю один небольшой лакомый кусочек. Многие комментарии задаются вопросом, почему int, long и почти любой другой числовой тип также не следует этому правилу ... возвращать «больший» тип в ответ на арифметический.

Многие ответы были связаны с производительностью (ну, 32 бита быстрее, чем 8 бит). На самом деле, 8-битное число по-прежнему является 32-битным числом для 32-битного процессора ... даже если вы добавите два байта, фрагмент данных, с которым работает процессор, будет 32-битным независимо ... поэтому добавление int не будет будь «быстрее», чем добавление двух байтов ... все равно процессору. СЕЙЧАС добавление двух целых чисел БУДЕТ быстрее, чем добавление двух длинных чисел на 32-битном процессоре, потому что добавление двух длинных чисел требует больше микроопераций, так как вы работаете с числами шире, чем слово процессора.

Я думаю, что основная причина того, что байтовая арифметика приводит к целым числам, довольно ясна и прямолинейна: 8-битные просто не идут очень далеко! : D С 8 битами у вас есть беззнаковый диапазон от 0 до 255. Это не много места для работы ... вероятность того, что вы столкнетесь с ограничениями по байтам, ОЧЕНЬ высока при их использовании в арифметике. Однако вероятность того, что у вас закончатся биты при работе с целыми, длинными, двойными и т. Д., Значительно ниже ... достаточно мала, чтобы мы очень редко сталкивались с потребностью в большем количестве.

Автоматическое преобразование байта в int является логическим, потому что размер байта очень мал. Автоматическое преобразование из int в long, float в double и т. Д. нелогично, потому что эти числа имеют значительный масштаб.

person jrista    schedule 03.06.2009
comment
Это все еще не объясняет, почему byte - byte возвращает int или почему они не приводят к _3 _... - person KthProg; 17.10.2017
comment
Почему вам нужно, чтобы сложение возвращало другой тип, чем вычитание? Если byte + byte возвращает int, поскольку 255 + что-либо больше, чем может содержать байт, не имеет смысла иметь какой-либо байт минус любой другой байт, возвращающий что-либо, кроме int с точки зрения согласованности типа возвращаемого значения. - person jrista; 24.10.2017
comment
Я бы не стал, это просто показывает, что приведенная выше причина, вероятно, неверна. Если бы это было связано с подгонкой к результату, то вычитание byte вернет byte, а добавление байтов вернет short (byte + byte всегда будет соответствовать short). Если бы речь шла о согласованности, как вы говорите, то short все равно было бы достаточно для обеих операций, а не int. Очевидно, что существует множество причин, не обязательно все они хорошо продуманы. Или причина производительности, указанная ниже, может быть более точной. - person KthProg; 25.10.2017