((a + (b & 255)) & 255)는 ((a + b) & 255)와 같은가요?
일부 C ++ 코드를 탐색하고 있는데 다음과 같은 것을 발견했습니다.
(a + (b & 255)) & 255
두 배로 나를 짜증나게하고 다음과 같이 생각했습니다.
(a + b) & 255
( a
및 b
32 비트 부호없는 정수)
내 이론을 확인하기 위해 테스트 펼쳐보기 (JS)를 빠르게 작성했습니다.
for (var i = 0; i < 100; i++) {
var a = Math.ceil(Math.random() * 0xFFFF),
b = Math.ceil(Math.random() * 0xFFFF);
var expr1 = (a + (b & 255)) & 255,
expr2 = (a + b) & 255;
if (expr1 != expr2) {
console.log("Numbers " + a + " and " + b + " mismatch!");
break;
}
}
펼쳐 내 가설을 (두 작업은 동일) 1) 때문에, 나는 아직도 그것을 신뢰하지 않는 확인하는 동안 임의 2) 나는 수학자가 아니에요, 나는 내가 하는 아무 생각이 없다 .
또한 Lisp-y 제목에 대해 죄송합니다. 자유롭게 편집하십시오.
그들은 동일합니다. 다음은 증거입니다.
먼저 신원 확인 (A + B) mod C = (A mod C + B mod C) mod C
을 ( a & 255
를) 위해 서있는 것에서 문제를 다시 설명하겠습니다 a % 256
. a
서명되지 않은 현상기 때문에 이것은 사실 입니다.
그래서 (a + (b & 255)) & 255
입니다(a + (b % 256)) % 256
이 같은입니다 (a % 256 + b % 256 % 256) % 256
(제가 언급 한 내용을 참조한 동일을 적용한 : 노트 mod
와 %
. 부호없는 형식에 대한)
이것은 단순화 (a % 256 + b % 256) % 256
되는 (a + b) % 256
(정체성을 다시 적용)합니다. 그런 다음 비트 연산자를 다시 넣어
(a + b) & 255
증거를 완성합니다.
부호없는 결과를 생성하기위한 부호없는 숫자의 위치 덧셈, 빼기 및 곱셈에서 입력의 유효 숫자가 많음 결과의 중요도가 낮은 숫자에 영향을주지. 이 십진 산술만큼이나 이진 산술에도 적용됩니다. "2의 보수"는 적용되지 않는 산술에도 적용됩니다.
그러나 우리는 이진 산술에서 규칙을 가져 와서 적용 할 때주의해야합니다. (저는 C ++ 이이 작업에 C와 같은 규칙을 가지고 C 산술에는 우리를 넘어 뜨릴 수있는 신비한 규칙이 있기 때문입니다.) 최상급. C의 부호없는 산술은 이진 랩 어라운드 규칙을 간단한지만 부호없는 산술 오버플로는 정의되지 않은 동작입니다. 어떤 상황에서는 C가 서명되지 않은 유형을 (서명 된) int로 자동으로 "승격"합니다.
C에서 정의되지 않은 동작은 특히 교활 할 수 있습니다. 멍청한 컴파일러 (또는 최적화 수준의 컴파일러)는 이진 산술에 대한 이해를 바탕으로 예상 한 작업을 수행 할 가능성이있는 최적화 컴파일러는 이상한 방식으로 코드를 손상시킬 수 있습니다.
따라서 질문의 공식으로 돌아 가면 동등성은 피연산자 유형에 따라 따라합니다.
크기가 크기보다 크거나 같은 부호없는 정수 int
이면 더하기 연산자의 오버플로 동작은 단순 이진 랩 어라운드로 잘 정의됩니다. 더하기 연산 전에 한 피연산자의 상위 24 비트를 마스킹할지 여부는 결과의 하위 비트에 영향을주지 않습니다.
크기가 int
다음보다 작은 부호없는 정수이면 (signed)로 승격 int
됩니다. 부호있는 정수의 오버플로는 정의되지 않은 동작이지만 모든 플랫폼에서 서로 다른 정수 유형 상관 크기 차이가 너무 커서 두 개의 추가해도되는 오버플로가 발생하지 않습니다. 그래서 다시 우리는 단순한 이진 산술 인수로 돌아가서 문장이 동등하고 할 수 있습니다.
크기가 int보다 작은 부호있는 정수이면 다시 오버플로가 구현있는 정수이면 두 보완 보완에서 표준 이진 산술 인수에 의존하여 동등 할 수 있습니다. 부호 크기 또는 구현을 보완하는 동등하지 않습니다.
OTOH 경우 a
및 b
크기가 INT의 크기보다 크거나 같은 부호있는 정수이면 2 개의 보완 구현에서도 한 명령문이 잘 정의되고 다른 명령문은 정의되지 않은 동작이되는 경우가 있습니다 .
기본형 : a & 255 == a % 256
unsigned a
.
가 부호 a
같이 다시 작성할 수 있습니다 m * 0x100 + b
일부가 서명 m
, b
, 0 <= b < 0xff
, 0 <= m <= 0xffffff
. 두 정의에서 a & 255 == b == a % 256
.
또한 다음이 필요합니다.
- 분배 속성 :
(a + b) mod n = [(a mod n) + (b mod n)] mod n
- 수학적으로 부호없는 덧셈의 정의 :
(a + b) ==> (a + b) % (2 ^ 32)
그러므로:
(a + (b & 255)) & 255 = ((a + (b & 255)) % (2^32)) & 255 // def'n of addition
= ((a + (b % 256)) % (2^32)) % 256 // lemma
= (a + (b % 256)) % 256 // because 256 divides (2^32)
= ((a % 256) + (b % 256 % 256)) % 256 // Distributive
= ((a % 256) + (b % 256)) % 256 // a mod n mod n = a mod n
= (a + b) % 256 // Distributive again
= (a + b) & 255 // lemma
네, 사실입니다. 32 비트 부호없는 정수용입니다.
다른 정수 유형은 어떻습니까?
- 64 비트 부호없는 정수의 경우, 모든 위의 단지 대체 단지뿐만 아니라 적용
2^64
을 위해2^32
. - 8 비트 및 16 비트 부호없는 정수의 경우 추가에는
int
. 이것은int
분명히 이러한 작업에서 오버플로되거나 부정적이지 않으므로 모두 유효합니다. - 대한 서명 경우 하나의 정수,
a+b
또는a+(b&255)
오버 플로우, 그것은 정의되지 않은 동작입니다. 따라서 평등은 유지 될 수 없습니다.(a+b)&255
정의되지 않은 동작이 있지만 그렇지 않은 경우(a+(b&255))&255
가 있습니다.
네, (a + b) & 255
괜찮습니다.
학교에서 덧셈을 기억하십니까? 숫자로 숫자를 추가하고 다음 숫자 열에 캐리 값을 추가합니다. 나중 (더 중요한) 숫자 열이 이미 처리 된 열에 영향을 미칠 수있는 방법은 없습니다. 이 때문에 결과에서만 숫자를 제로 아웃하거나 인수에서 첫 번째로 0을 지우면 차이가 없습니다.
위의 내용이 항상 사실은 아닙니다. C ++ 표준은이를 깨뜨리는 구현을 허용합니다.
이러한 Deathstation 9000 : - ) 33 비트를 사용하는 것 int
영업 의미하는 경우, unsigned short
"32 비트 부호없는 정수"로. unsigned int
의미가 있다면 DS9K는 int
32 비트 unsigned int
와 패딩 비트가 있는 32 비트를 사용해야합니다 . (부호없는 정수는 §3.9.1 / 3에 따라 서명 된 정수와 동일한 크기를 가져야하며 패딩 비트는 §3.9.1 / 1에서 허용됩니다.) 크기 및 패딩 비트의 다른 조합도 작동합니다.
내가 말할 수있는 한, 이것이 그것을 깨는 유일한 방법입니다.
- 정수 표현은 "순수 이진"인코딩 체계 (§3.9.1 / 7 및 각주)를 사용해야하며 패딩 비트와 부호 비트를 제외한 모든 비트는 2n 의 값을 제공해야합니다.
- int 승격은
int
소스 유형 (§4.5 / 1)의 모든 값을 나타낼 수있는 경우에만 허용되므로 값에int
기여하는 32 비트 이상과 부호 비트가 있어야합니다. - 이
int
있다는 이유 다른 부가 오버플 수는 32 이상 (부호 비트 수를 계산하지 않음) 이상의 값 비트를 가질 수 없다.
당신은 이미 현명한 답을 가지고 있습니다. 부호없는 산술은 모듈로 산술이므로 결과는 그대로 유지되며 수학적으로 증명할 수 있습니다.
하지만 컴퓨터의 멋진 점은 컴퓨터가 빠르다는 것입니다. 사실, 그들은 너무 빠르기 때문에 모든 유효한 32 비트 조합을 합당한 시간 안에 열거 할 수 있습니다 (64 비트로 시도하지 마십시오).
그래서, 당신의 경우, 저는 개인적으로 그것을 컴퓨터에 던지는 것을 좋아합니다. 수학적 증명이 정확 하고 사양 1 의 세부 사항을 감독하지 않았다는 것보다 프로그램이 정확하다는 것을 스스로 확신하는 데 걸리는 시간이 적습니다 .
#include <iostream>
#include <limits>
int main() {
std::uint64_t const MAX = std::uint64_t(1) << 32;
for (std::uint64_t i = 0; i < MAX; ++i) {
for (std::uint64_t j = 0; j < MAX; ++j) {
std::uint32_t const a = static_cast<std::uint32_t>(i);
std::uint32_t const b = static_cast<std::uint32_t>(j);
auto const champion = (a + (b & 255)) & 255;
auto const challenger = (a + b) & 255;
if (champion == challenger) { continue; }
std::cout << "a: " << a << ", b: " << b << ", champion: " << champion << ", challenger: " << challenger << "\n";
return 1;
}
}
std::cout << "Equality holds\n";
return 0;
}
이는 32 비트 공간에서 a
및의 가능한 모든 값을 열거 b
하고 동등성이 유지되는지 여부를 확인합니다. 그렇지 않으면 작동하지 않은 케이스를 인쇄하여 온 전성 검사로 사용할 수 있습니다.
그리고 Clang에 따르면 : Equality hold .
또한 산술 규칙이 비트 폭에 구애받지 않고 ( int
비트 폭 이상)이 동등성은 64 비트 및 128 비트를 포함하여 32 비트 이상의 부호없는 정수 유형에 대해 유지됩니다.
참고 : 컴파일러는 합리적인 시간 프레임에서 모든 64 비트 패턴을 어떻게 열거 할 수 있습니까? 그럴 순 없어. 루프가 최적화되었습니다. 그렇지 않으면 우리는 모두 처형이 끝나기 전에 죽었을 것입니다.
처음에는 16 비트의 부호없는 정수에 대해서만 증명했습니다. 불행히도 C ++는 작은 정수 (보다 작은 비트 폭 int
)가 처음으로 int
.
#include <iostream>
int main() {
unsigned const MAX = 65536;
for (unsigned i = 0; i < MAX; ++i) {
for (unsigned j = 0; j < MAX; ++j) {
std::uint16_t const a = static_cast<std::uint16_t>(i);
std::uint16_t const b = static_cast<std::uint16_t>(j);
auto const champion = (a + (b & 255)) & 255;
auto const challenger = (a + b) & 255;
if (champion == challenger) { continue; }
std::cout << "a: " << a << ", b: " << b << ", champion: "
<< champion << ", challenger: " << challenger << "\n";
return 1;
}
}
std::cout << "Equality holds\n";
return 0;
}
그리고 다시 한번 Clang에 따르면 : Equality hold .
글쎄, 당신은 간다 :)
1 물론 프로그램이 정의되지 않은 동작을 실수로 트리거하는 경우 그다지 증명되지 않습니다.
빠른 대답은 다음과 같습니다. 두 표현 모두 동일합니다.
- 이후
a
와b
32 비트 부호없는 정수 결과도 오버플의 경우와 동일하다. unsigned arithmetic은 이것을 보장 합니다.
긴 대답은 다음과 같습니다. 이러한 표현이 다를 수있는 알려진 플랫폼은 없지만 통합 프로모션 규칙으로 인해 표준은이를 보장하지 않습니다.
의 타입의 경우
a
와b
(부호없는 32 개 비트 정수)보다 높은 순위를 갖는int
, 연산은 부호로서 수행 2 모듈로 연산된다 (32) , 모든 값 모두 식 대한 동일한 정의 결과 산출a
및b
.반대로
a
및 유형b
이보다 작은 경우int
둘 다로 승격되고int
계산이 부호있는 산술을 사용하여 수행됩니다. 여기서 오버플로는 정의되지 않은 동작을 호출합니다.경우
int
결과가 완벽하게 정의되어 있으므로, 오버 플로우 수도 상기 식 중 적어도 33 비트 값을 가지며, 양 식에 대해 동일한 값을 갖는다.경우
int
정확히 32 비트 값을 보유하고, 계산은 수 오버플 두 값의 예를 들면, 식a=0xFFFFFFFF
및b=1
두 식의 오버 플로우를 일으킬 것이다. 이를 방지하려면((a & 255) + (b & 255)) & 255
.
좋은 소식은 그러한 플랫폼이 없다는 것입니다 1 .
1 더 정확하게는 그러한 실제 플랫폼이 존재하지 않지만 이러한 동작을 나타내면서도 C 표준을 준수하도록 DS9K 를 구성 할 수 있습니다 .
오버플로가 없다고 가정하면 동일 합니다 . 두 버전 모두 오버플로에 대해 진정으로 면역이되지는 않지만 이중 버전과 버전이 더 잘 견딥니다. 이 경우 오버플로가 문제가되는 시스템은 알지 못하지만,이 경우 작성자가이를 수행하는 것을 볼 수 있습니다.
예, 산술로 증명할 수 있지만 더 직관적 인 답이 있습니다.
추가 할 때 모든 비트는 자신보다 더 중요한 부분에만 영향을줍니다. 덜 중요하지 않습니다.
따라서 추가하기 전에 더 높은 비트에 대해 수행하는 작업은 수정 된 가장 낮은 비트보다 덜 중요한 비트 만 유지하는 한 결과를 변경하지 않습니다.
증거는 사소하며 독자를위한 연습용으로 남겨 둡니다.
그러나 실제로 이것을 답으로 합법화하기 위해 첫 번째 코드 줄은 b
** 의 마지막 8 비트 (모두 b
0 으로 설정된 상위 비트 ) a
를 가져와 이것을 추가 한 다음 결과 설정의 마지막 8 비트 만 모두 더 높게 설정 한다고 말합니다. 0으로 비트.
두 번째 줄은 더 높은 비트가 모두 0 인 마지막 8 비트를 추가 a
하고 b
가져옵니다.
마지막 8 비트 만 결과에서 중요합니다. 따라서 마지막 8 비트 만 입력에서 중요합니다.
** 마지막 8 비트 = 8 LSB
또한 출력이 다음과 같을 것이라는 점도 흥미 롭습니다.
char a = something;
char b = something;
return (unsigned int)(a + b);
위와 같이 8 개의 LSB 만 중요하지만 unsigned int
다른 모든 비트가 0 인 결과는 입니다. 는 a + b
예상 된 결과를 생산, 오버 플로우됩니다.
참고 URL : https://stackoverflow.com/questions/40751662/is-ab-255-255-the-same-as-ab-255
'IT' 카테고리의 다른 글
.net 4.5에서 HttpClient를 사용하여 쿠키를 응답에서 벗어나려는 시도 (0) | 2020.09.01 |
---|---|
Collections.emptyList ()와 Collections.EMPTY_LIST의 차이점은 무엇입니까? (0) | 2020.09.01 |
CFLAGS 대 CPPFLAGS (0) | 2020.09.01 |
jquery, 클래스별로 다음 요소 찾기 (0) | 2020.09.01 |
iOS 배포 용 P12 인증서를 만드는 방법 (0) | 2020.08.31 |