바이트 + 바이트 = 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 배열 대신 바이트 배열을 사용하는 것이 더 빠릅니다 . 그러나 광범위한 바이트 캐스트는 코드를 통해 확산되어 훨씬 더 읽을 수 없게 만듭니다.
코드 스 니펫의 세 번째 줄 :
byte z = x + y;
실제로 의미
byte z = (int) x + (int) y;
따라서 바이트에는 + 연산이 없으며 바이트는 먼저 정수로 캐스트되고 두 정수를 더한 결과는 (32 비트) 정수입니다.
"어떻게 발생 하는가"의 관점에서, 다른 사람들이 말했듯이 바이트, sbyte, short 또는 ushort로 산술하기 위해 C #에 의해 정의 된 연산자가 없기 때문입니다. 이 답변은 왜 그 연산자가 정의되지 않았는 지에 관한 것입니다 .
나는 그것이 기본적으로 성능을 위해서라고 생각합니다. 프로세서는 32 비트로 매우 빠르게 산술 연산을 수행합니다. 결과에서 바이트로의 변환을 자동으로 수행 할 수는 있지만 실제로 해당 동작을 원하지 않는 경우 성능이 저하 될 수 있습니다.
나는 생각 이 주석 C #을 기준 중 하나에 언급되어있다. 보고있는 중 ...
편집 : 짜증나게, 나는 이제 주석이 달린 ECMA C # 2 사양, 주석이 달린 MS C # 3 사양 및 주석 CLI 사양 을 살펴 보았으며, 내가 볼 수있는 한 아무도 언급 하지 않았습니다 . 나는 위에 주어진 이유를 보았을 것이라고 확신 하지만, 내가 어디 있는지 알면 날아갑니다. 사과, 참조 팬 :(
나는 이것을 어딘가에 본 적이 있다고 생각 했다. 에서 이 문서 올드 뉴 살아라 :
'바이트'에 대한 작업이 '바이트'인 판타지 세계에 살았다 고 가정 해보십시오.
byte b = 32;
byte c = 240;
int i = b + c; // what is i?
이 환상의 세계에서 i의 가치는 16입니다! 왜? + 연산자에 대한 두 피연산자가 모두 바이트이므로 합계 "b + c"는 바이트로 계산되므로 정수 오버플로로 인해 16이됩니다. (앞서 언급했듯이 정수 오버플로는 새로운 보안 공격 벡터입니다.)
편집 : Raymond는 본질적으로 C와 C ++이 원래 취한 접근법을 방어하고 있습니다. 이 의견에서 그는 언어 역 호환성이라는 이유로 C #이 동일한 접근 방식을 취한다는 사실을 변호합니다.
씨#
ECMA-334에 따르면 추가는 int + int, uint + uint, long + long 및 ulong + ulong (ECMA-334 14.7.4)에서만 법적으로 정의됩니다. 따라서, 이들은 14.4.2와 관련하여 고려해야 할 후보 작업입니다. byte에서 int, uint, long 및 ulong으로 암시 적 캐스트가 있기 때문에 모든 추가 함수 멤버는 14.4.2.1에서 적용 가능한 함수 멤버입니다. 14.4.2.3의 규칙에 따라 최상의 암시 적 캐스트를 찾아야합니다.
int (T1)에 캐스팅 (C1)을 캐스팅 (C2)을 uint (T2) 또는 ulong (T2)에 캐스팅하는 것보다 낫습니다.
- T1이 int이고 T2가 uint이거나 ulong이면 C1이 더 나은 변환입니다.
int에서 long으로의 암시 적 캐스트가 있기 때문에 (C1)에서 int (T1)로 캐스트하는 것이 낫습니다 (C2).
- 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 파티션 3 섹션 1.6)
바이트를 추가하고 결과를 바이트로 다시 자르는 비 효율성을 나타내는 대답이 올바르지 않습니다. x86 프로세서에는 8 비트 수량의 정수 연산을 위해 특별히 설계된 명령어가 있습니다.
실제로 x86 / 64 프로세서의 경우 32 비트 또는 16 비트 작업을 수행하면 디코딩해야하는 피연산자 접두사 바이트로 인해 64 비트 또는 8 비트 작업보다 효율성이 떨어집니다. 32 비트 시스템에서 16 비트 작업을 수행하면 동일한 결과가 발생하지만 8 비트 작업을위한 전용 opcode가 여전히 있습니다.
많은 RISC 아키텍처에는 유사한 기본 단어 / 바이트 효율적인 명령어가 있습니다. 일반적으로 저장 및 변환에서 부호있는 값의 비트 길이를 갖지 않는 것.
다시 말해서,이 결정은 하드웨어의 비 효율성으로 인한 것이 아니라 바이트 유형이 무엇인지에 대한 인식을 기반으로해야합니다.
바이트가 실제로 + 연산자에 과부하가 걸리지 않는 방법에 대해 Jon Skeet에서 무언가를 읽은 것을 기억합니다 (지금 찾을 수 없으며 계속 볼 것입니다). 실제로 샘플에서와 같이 2 바이트를 추가 할 때 각 바이트는 실제로 암시 적으로 int로 변환됩니다. 그 결과는 분명히 int입니다. 이제 이것이 이런 식으로 설계된 이유에 관해서는 Jon Skeet 자신이 게시 할 때까지 기다립니다. :)
편집 : 찾았어요! 바로이 주제에 대한 다양한 정보를 원하시면 여기 .
이것은 오버플로와 캐리 때문입니다.
두 개의 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 #이 실제로 operator+
정의 된 on int
( 블록 int
에 있지 않으면를 반환)을 호출 checked
하고 암시 적으로 bytes
/ shorts
to를 모두 캐스팅한다는 것입니다 ints
. 이것이 동작이 일치하지 않는 이유입니다.
이것은 아마도 언어 디자이너 측에서 실질적인 결정이었을 것입니다. 결국 int는 32 비트 부호있는 정수인 Int32입니다. int보다 작은 유형에서 정수 연산을 수행 할 때마다 대부분의 32 비트 CPU에 의해 32 비트 부호있는 int로 변환됩니다. 그것은 작은 정수가 넘칠 가능성과 결합하여 아마도 거래를 봉인했을 것입니다. 오버 플로우 / 언더 플로우를 지속적으로 확인하는 번거 로움을 덜고 바이트의 표현식의 최종 결과가 범위 내에있을 때 일부 중간 단계에서 범위를 벗어났다는 사실에도 불구하고 올바른 결과를 얻습니다. 결과.
또 다른 생각 : 이러한 유형의 오버 플로우 / 언더 플로우는 가장 가능성이 높은 대상 CPU에서 자연적으로 발생하지 않기 때문에 시뮬레이션해야합니다. 왜 귀찮게?
이것은 대부분이 주제와 관련된 내 대답이며, 여기 비슷한 질문에 먼저 제출 되었습니다 .
Int32보다 작은 정수를 갖는 모든 연산은 기본적으로 계산하기 전에 32 비트로 반올림됩니다. 결과가 Int32 인 이유는 단순히 계산 후에 그대로 두는 것입니다. MSIL 산술 opcode를 확인하면 작동하는 정수 유형은 Int32 및 Int64뿐입니다. "디자인 상"입니다.
결과를 Int16 형식으로 되돌리려면 코드로 캐스트를 수행하거나 컴파일러가 (전후 적으로) "후드"아래에서 변환을 내보내는 것은 무의미합니다.
예를 들어 Int16 산술을 수행하려면
short a = 2, b = 3;
short c = (short) (a + b);
이 두 숫자는 32 비트로 확장되고 추가 된 다음 16 비트로 잘려서 MS가 의도 한 방식입니다.
짧은 (또는 바이트) 사용의 이점은 주로 대량의 데이터 (그래픽 데이터, 스트리밍 등)가있는 경우 스토리지입니다.
바이트에 대한 추가가 정의되지 않았습니다. 그래서 그들은 추가를 위해 int로 캐스팅됩니다. 대부분의 수학 연산 및 바이트에 해당됩니다. (이것이 이전 언어로 사용되었던 방식입니다. 오늘 그것이 사실이라고 가정합니다).
나는 그것이 어떤 작업이 더 일반적인 지에 대한 디자인 결정이라고 생각합니다 ... byte + byte = byte 경우 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 사이의 성능을 테스트했습니다.
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);
}
}
결과 :
byte : 3.57s 157mo, 3.71s 171mo, 3.74s 168mo with CPU ~ = 30 %
int : 4.05s 298mo, 3.92s 278mo, 4.28 294mo with CPU 포함 ~ = 27 %
결론 :
byte는 CPU를 더 많이 사용하지만 les 메모리 비용이 높고 더 빠릅니다 (할당 바이트가 적기 때문에)
다른 모든 위대한 의견 외에도, 나는 작은 작은 음식을 추가 할 것이라고 생각했습니다. 많은 의견이 왜 int, long 및 기타 다른 숫자 유형 이이 규칙을 따르지 않는지 궁금해했습니다 ... 산술에 대한 응답으로 "더 큰"타입을 반환하십시오.
많은 대답은 성능과 관련이 있습니다 (32 비트는 8 비트보다 빠릅니다). 실제로 8 비트 숫자는 32 비트 CPU의 32 비트 숫자입니다 .... 2 바이트를 추가하더라도 CPU가 작동하는 데이터 청크는 32 비트가됩니다 ... 따라서 int 추가는 2 바이트를 추가하는 것보다 "빠른"것입니다. CPU와 동일합니다. 이제 두 개의 정수를 추가하면 32 비트 프로세서에 두 개의 정수를 추가하는 것보다 빠릅니다. 두 개의 정수를 추가하면 프로세서 워드보다 넓은 수의 작업을하기 때문에 더 많은 마이크로 옵스가 필요하기 때문입니다.
바이트 산술이 정수를 초래하는 근본적인 이유는 매우 명확하고 직선적이라고 생각합니다. 8 비트는 그리 멀지 않습니다! : D 8 비트의 경우 부호없는 범위는 0-255입니다. 그것은 작업 할 공간 이 많지 않습니다 ... 바이트 제한에 빠질 가능성은 산술에 사용할 때 매우 높습니다. 그러나 int, long 또는 double 등으로 작업 할 때 비트가 부족할 확률은 크게 낮아서 더 이상 필요하지 않습니다.
바이트 의 스케일이 너무 작기 때문에 byte에서 int 로의 자동 변환은 논리적 입니다. int에서 long으로, float에서 double로 등으로 자동 변환하는 것은 그 숫자의 규모가 크기 때문에 논리적이지 않습니다 .
참고 URL : https://stackoverflow.com/questions/941584/byte-byte-int-why
'development' 카테고리의 다른 글
빈 Pandas DataFrame을 만든 다음 채우시겠습니까? (0) | 2020.02.29 |
---|---|
AngularJS 컨트롤러간에 데이터 공유 (0) | 2020.02.29 |
PreferenceActivity에서 "addPreferencesFromResource"대신 무엇을 사용해야합니까? (0) | 2020.02.29 |
Spring Framework에서 applicationContext.xml과 spring-servlet.xml의 차이점 (0) | 2020.02.29 |
Java의 HTTP URL 주소 인코딩 (0) | 2020.02.29 |