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) выполняется быстрее (из-за хитов кэша). Но обширные байты, распространяемые через код, делают его гораздо более нечитаемым.

Третья строка вашего fragmentа кода:

 byte z = x + y; 

фактически означает

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

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

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

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

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

EDIT: Раздражающе, теперь я просмотрел аннотированную спецификацию ECMA C # 2, аннотированную спецификацию MS C # 3 и спецификацию CLI annotations, и ни один из них не упоминает об этом, насколько я могу судить. Я уверен, что видел причину, приведенную выше, но меня взрывают, если я знаю, где. Извинения, ссылки поклонников 🙁

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

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

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

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

EDIT : Раймонд защищает, по сути, подход 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), потому что:

  • Если 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)

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

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

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

Многие архитектуры RISC имеют аналогичные собственные инструкции по использованию слова / байта. Те, у которых обычно нет типа «store-and-convert-to-signed-value-of-some-bit-length».

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

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

EDIT: Найди его! Отличная информация по этой теме.

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

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

Пример:

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

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

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

Язык C # Spec

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

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

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

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

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

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

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

 short a = 2, b = 3; short c = (short) (a + b); 

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

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

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

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

Из кода .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); 

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

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

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

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

  • Как преобразовать NSData в массив байтов в iPhone?
  • Результат Struts2 INPUT: как он работает? Как обрабатываются ошибки преобразования / проверки?
  • В чем разница между Convert.ToInt32 и (int)?
  • Преобразование типа Spring MVC: PropertyEditor или Converter?
  • Ведущие нули для Int в Swift
  • Установка свойства путем отражения со строковым значением
  • Как преобразовать String в InputStream в Java?
  • Как преобразовать String в int в Java?
  • Разница между Convert.ToString () и .ToString ()
  • Самый простой способ конвертировать int в строку в C ++
  • Как преобразовать QString в std :: string?
  • Interesting Posts
    Давайте будем гением компьютера.