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

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

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

Ответ 1

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

byte z = x + y;

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

EDIT: Раймонд защищает, по сути, подход C и С++ первоначально. В комментариях он защищает тот факт, что С# использует тот же подход, исходя из совместимости языка.

Ответ 4

С#

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.

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

CLI

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

Пример:

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

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

Ответ 8

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

Язык С# Spec

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

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

Например, для выполнения арифметики Int16:

short a = 2, b = 3;

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

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

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

Ответ 12

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

Ответ 13

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

Ответ 14

В дополнение ко всем остальным замечательным комментариям, я думал, что добавлю один маленький лакомый кусочек. Многие комментарии задавались вопросом, почему 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 и т.д. нелогично, потому что эти цифры имеют значительный масштаб.

Ответ 15

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

Ответ 16

Это потому, что оператор + предположим, что .Net и jvm, что когда-либо был компилятором, который является int mean (INTEGER) Datatype и не может быть явно преобразован в байты, он будет работать для вас.

byte b = 10;
b  = (byte) (b+120);
System.out.println(b);

фон

b+1;
ax(int,byte b ,int 1);
b++;
b = (type of b) (b+1);