Глядя на этот код 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) происходит быстрее (из-за попаданий в кэш). Но обширные броски байтов, распространяемые по коду, делают его намного более нечитаемым.
c#
type-conversion
Роберт Картейно
источник
источник
byte1 | byte2
вовсе не рассматривает их как числа. Это относится к ним как к шаблонам битов. Я понимаю вашу точку зрения, но так получилось, что каждый раз, когда я выполнял какие-либо арифметические действия с байтами в C #, я фактически рассматривал их как биты, а не числа, и такое поведение всегда мешает.Ответы:
Третья строка вашего кода:
на самом деле означает
Таким образом, в байтах нет операции +, байты сначала приводятся к целым числам, а результатом сложения двух целых чисел является (32-разрядное) целое число.
источник
int
происходит автоматическое продвижение на основе компилятора : stackoverflow.com/a/43578929/4561887С точки зрения «почему это вообще происходит», это потому, что в C # нет никаких операторов, определенных для арифметики с байтами, sbyte, short или ushort, как уже говорили другие. Этот ответ о том, почему эти операторы не определены.
Я считаю, что это в основном ради производительности. Процессоры имеют встроенные операции для очень быстрой арифметики с 32 битами. Выполнение преобразования обратно из результата в байт может автоматически выполнить , но это приведет к снижению производительности в том случае, если вы на самом деле не хотите такого поведения.
Я думаю, что это упоминается в одном из аннотированных стандартов C #. Ищу...
РЕДАКТИРОВАТЬ: досадно, я теперь просмотрел аннотированную спецификацию C # 2 ECMA, аннотированную спецификацию MS C # 3 и спецификацию CLI аннотации, и ни один из них не упомянул это, насколько я вижу. Я уверен, что видел причину, приведенную выше, но я взорван, если знаю, где. Извиняюсь, референт фанатов :(
источник
Я думал, что видел это где-то раньше. Из этой статьи, Старое Новое :
РЕДАКТИРОВАТЬ : Раймонд, по сути, защищает подход C и C ++ первоначально. В комментариях он защищает тот факт, что C # использует тот же подход на основе обратной совместимости языка.
источник
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), потому что:
Преобразование (C1) в int (T1) лучше, чем приведение (C2) к long (T2), потому что существует неявное приведение от int к long:
Следовательно, используется функция int + int, которая возвращает int.
Это очень долгий путь, чтобы сказать, что он очень глубоко скрыт в спецификации C #.
CLI
CLI работает только на 6 типах (int32, native int, int64, F, O и &). (ECMA-335 раздел 3 раздел 1.5)
Байт (int8) не относится к таким типам, и перед добавлением автоматически приводится к типу int32. (ECMA-335 раздел 3 раздел 1.6)
источник
byte3 = byte1 And byte2
без приведения, но бесполезно сгенерирует исключение времени выполнения, если получитint1 = byte1 + byte2
значение больше 255. Я не знаю, позволят ли какие-либо языки,byte3 = byte1+byte2
и сгенерирует исключение, если оно превысит 255, но не сгенерирует исключение, еслиint1 = byte1+byte2
получит значение в диапазоне 256-510.Ответы, указывающие на некоторую неэффективность добавления байтов и усечения результата обратно в байт, неверны. Процессоры x86 имеют инструкции, специально предназначенные для целочисленной работы в 8-битных количествах.
Фактически, для процессоров x86 / 64 выполнение 32-битных или 16-битных операций менее эффективно, чем 64-битных или 8-битных операций из-за байта префикса операнда, который должен быть декодирован. На 32-разрядных компьютерах выполнение 16-разрядных операций влечет за собой то же наказание, но для 8-разрядных операций все еще существуют специальные коды операций.
Многие архитектуры RISC имеют схожие собственные эффективные инструкции. Те, которые, как правило, не имеют длины "хранить и преобразовать в значение со знаком в некотором бите".
Другими словами, это решение должно было основываться на восприятии того, для чего предназначен тип байта, а не на основе неэффективности аппаратного обеспечения.
источник
byte
(иchar
), но не дляshort
которого семантически явно число.Я помню, как однажды читал что-то от Джона Скита (сейчас я не могу его найти, я буду продолжать искать) о том, что байт на самом деле не перегружает оператор +. Фактически, при добавлении двух байтов, как в вашем примере, каждый байт фактически неявно преобразуется в int. Результатом этого, очевидно, является int. Теперь о том, ПОЧЕМУ это было разработано таким образом, я буду ждать, пока сам Джон Скит отправит сообщение :)
РЕДАКТИРОВАТЬ: нашел это! Отличная информация об этой самой теме здесь .
источник
Это из-за переполнения и переноски.
Если вы добавите два 8-битных числа, они могут переполниться в 9-й бит.
Пример:
Я не знаю наверняка, но я предполагаю, что
ints
,longs
и мнеdoubles
дают больше места, потому что они довольно большие, как есть. Кроме того, они кратны 4, что более эффективно для компьютеров, поскольку ширина внутренней шины данных составляет 4 байта или 32 бита (64 бита становятся все более распространенными в настоящее время). Байт и шорт немного более неэффективны, но они могут сэкономить место.источник
Из спецификации языка C # 1.6.7.5 7.2.6.2 Двоичные числовые продвижения он преобразует оба операнда в int, если не может вписать его в несколько других категорий. Я предполагаю, что они не перегружали оператор + для получения байта в качестве параметра, но хотели, чтобы он работал как обычно, поэтому они просто используют тип данных int.
Спецификация языка C #
источник
Я подозреваю, что C # на самом деле вызывает
operator+
определенное значениеint
(которое возвращаетint
исключение, если вы не вchecked
блоке) и неявно приводит оба вашихbytes
/shorts
кints
. Вот почему поведение выглядит противоречивым.источник
Вероятно, это было практическое решение со стороны языковых дизайнеров. В конце концов, int - это Int32, 32-разрядное целое число со знаком. Всякий раз, когда вы выполняете целочисленную операцию над типом, меньшим, чем int, он все равно будет преобразован в 32-битное целое число со знаком большинства большинства 32-битных процессоров. Это, в сочетании с вероятностью переполнения маленьких целых чисел, вероятно, заключило сделку. Это избавляет вас от рутинной проверки на предмет избыточного / недостаточного потока, и когда конечный результат выражения в байтах окажется в диапазоне, несмотря на тот факт, что на каком-то промежуточном этапе он будет вне диапазона, вы получите правильный результат.
Другая мысль: переполнение / переполнение этих типов должно быть смоделировано, поскольку оно не будет происходить естественным образом на наиболее вероятных целевых процессорах. Зачем беспокоиться?
источник
Это по большей части мой ответ, который относится к этой теме, и представлен сначала на аналогичный вопрос здесь. .
Все операции с целыми числами, меньшими, чем Int32, округляются до 32 бит до вычисления по умолчанию. Причина, по которой результатом является Int32, заключается в том, чтобы просто оставить его как есть после расчета. Если вы проверяете арифметические коды операций MSIL, то единственными целыми числовыми типами, с которыми они работают, являются Int32 и Int64. Это "по замыслу".
Если вы хотите получить результат обратно в формате Int16, это не имеет значения, если вы выполняете приведение в коде, или компилятор (гипотетически) выдает преобразование «под капотом».
Например, чтобы сделать арифметику Int16:
Два числа расширились бы до 32 битов, были бы добавлены, а затем урезаны до 16 битов, как MS и планировала.
Преимущество использования коротких (или байтовых) данных - это, прежде всего, хранение в тех случаях, когда у вас большие объемы данных (графические данные, потоковая передача и т. Д.)
источник
Добавление не определено для байтов. Поэтому они приводятся к int для дополнения. Это верно для большинства математических операций и байтов. (обратите внимание, что так было на старых языках, я полагаю, что сегодня это так).
источник
Я думаю, что это проектное решение о том, какая операция была более распространенной ... Если byte + byte = byte, возможно, гораздо больше людей будет обеспокоено необходимостью приводить к int, когда в качестве результата требуется int.
источник
Из кода .NET Framework:
Упростите с .NET 3.5 и выше:
Теперь вы можете сделать:
источник
Я проверил производительность между байтом и целым числом.
Со значениями int:
С байтовыми значениями:
Вот результат:
байт: 3,57 с 157 мес., 3,71 с 171 мес., 3,74 с 168 мес. С ЦП ~ = 30%
int: 4,05 с 298 мес., 3,92 с 278 мес., 4,28 294 мес. С ЦП ~ = 27%
Вывод:
байт использует больше ЦП, но это стоит меньше памяти и быстрее (возможно потому, что выделяется меньше байтов)
источник
В дополнение ко всем другим замечательным комментариям, я подумал, что добавлю один маленький кусочек. Многие комментарии задавались вопросом, почему int, long и почти любой другой числовой тип также не следуют этому правилу ... возвращают «больший» тип в ответ на арифметику.
Многие ответы были связаны с производительностью (ну, 32 бита быстрее, чем 8 бит). На самом деле, 8-битное число по-прежнему 32-битное число для 32-битного ЦП .... даже если вы добавите два байта, кусок данных, на котором работает процессор, будет 32-битным независимо, поэтому добавление целых не будет Быть "быстрее", чем добавлять два байта ... это все равно для процессора. ТЕПЕРЬ, добавление двух целых будет БЫСТРЕЕ, чем добавление двух длинных на 32-битном процессоре, потому что добавление двух длинных требует больше микроопераций, так как вы работаете с числами шире, чем у процессоров.
Я думаю, что основная причина побуждения байтовой арифметики к целым числам довольно ясна и прямолинейна: 8 бит просто не идут очень далеко! : D С 8 битами у вас есть диапазон без знака 0-255. Это не много места для работы ... вероятность того, что вы столкнетесь с байтовыми ограничениями, ОЧЕНЬ высока при использовании их в арифметике. Однако вероятность того, что у вас закончатся биты при работе с целыми, длинными или двойными и т. Д., Значительно ниже ... настолько мала, что мы очень редко сталкиваемся с необходимостью большего.
Автоматическое преобразование из байта в int логично, потому что масштаб байта очень мал. Автоматическое преобразование из int в long, float в double и т. Д. Не логично, поскольку эти числа имеют значительный масштаб.
источник
byte - byte
возвращаетсяint
, или почему они не бросаютshort
...byte + byte
возвращаетсяint
, потому что 255 + что-либо больше, чем может содержать байт, не имеет смысла, чтобы какой-либо байт, кроме любого другого байта, возвращал что-либо кроме int с точки зрения согласованности возвращаемого типа.byte
вычитание вернуло бы abyte
, а сложение байтов вернуло быshort
(byte
+byte
всегда будет вписываться в ashort
). Если бы речь шла о последовательности, как вы говорите, тоshort
все равно было бы достаточно для обеих операций, а неint
. Понятно, что существует множество причин, но не все они обязательно продуманы. Или причина производительности, приведенная ниже, может быть более точной.