'올바른' 부호 없는 정수 비교
우리는 C을 알고 , 서 , 는, C/C++ /를 는합니다.-1 > 2u == true
'' 를 합니다.
제 질문은 사람들이 익숙한 만큼 많은 아키텍처를 고려하면 더 효율적이라는 것입니다.분명히 인텔과 ARM이 더 비중이 높습니다.
주어진:
int x;
unsigned int y;
if (x < y) {}
홍보하는 것이 더 나을까요?
x < y => (int64)x < (int64)y
아니면 다음과 같은 두 가지 비교를 수행하는 것이 더 나을까요?
x < y => (x < 0 || x < y)
전자는 0 확장, 부호 확장 및 1개의 비교+ 분기를 의미하며, 후자는 부호 확장 연산이 필요하지 않고 2개의 연속적인 cmp+ 분기를 의미합니다.
전통적인 통념에 따르면 분기는 신호 연장보다 더 비싸며, 두 번째 경우에는 연장과 단일 비교 사이에 교착 상태가 있는 반면, 두 번째 경우에는 일부 아키텍처가 두 가지 비교를 파이프라인으로 연결할 수 있지만 그 다음에는 조건부 분기가 2개 있다고 가정할 수 있습니다.
또 다른 경우가 있는데, 서명되지 않은 값은 서명된 유형보다 작은 유형입니다. 즉, 서명된 유형의 길이에 대한 제로 확장 한 번으로 수행할 수 있고, 그 다음에는 비교 한 번으로 비교할 수 있습니다.이 경우 extend + cmp 버전을 사용하는 것이 좋습니까? 아니면 2-comparison 방법이 여전히 선호됩니까?
인텔? ARM?다른 분들은?정답이 있는지는 모르겠지만, 사람들의 의견을 듣고 싶습니다.낮은 수준의 성능은 요즘 특히 Intel과 점점 더 많은 ARM에서 예측하기 어렵습니다.
편집:
또한 아키텍처 가로 폭과 동일한 크기의 유형을 선택할 수 있는 한 가지 분명한 해결책이 있습니다. 이 경우 프로모션 자체가 효율적으로 수행될 수 없으므로 2-비교 솔루션이 선호된다는 것은 분명합니다.y.int
이 는 32하며,할 수 .short
32비트 플랫폼에 적용되는 연습의 경우.
편집 2:
요, 요.u
인에-1 > 2u
! >_<
편집 3:
나는 비교 결과가 실제 분기라고 가정하고 결과가 부울로 반환되지 않는 것으로 상황을 수정하고자 합니다.이렇게 구조적인 모습을 선호합니다. 결과가 불 대 분기일 때 다른 일련의 순열이 있다는 흥미로운 점이 있습니다.
int g;
void fun(int x, unsigned in y) { if((long long)x < (long long)y) g = 10; }
void gun(int x, unsigned in y) { if(x < 0 || x < y) g = 10; }
으로 할 때 됩니다.if
;)
C/C++는 단일 비교를 통해 전체 서명된 int/unsigned int를 수행할 방법이 없습니다.
int64로 승진하는 것이 비교를 두 번 하는 것보다 더 빨랐다면 저는 놀랄 것입니다.내 두을 잘 (로음)합니다를 .) 하거나:(x < 0) | (x < y)
제 보다 큰 특별한 하지 이 있다는 .) 으로, 입니다합니다.(int64)x < (int64)y
실제로 완전한 int 비교를 할 가능성이 높습니다
결론적으로, 어떤 프로세서에서도 최고의 기계 코드를 생성할 수 있는 명령어는 없지만, 가장 일반적인 프로세서에서 가장 일반적인 컴파일러의 경우, 두 개의 비교 양식은 int64로 승격하는 양식보다 느리지 않을 것이라고 추측합니다.
편집: Godbolt에 대한 일부 비방은 ARM32에서 GCC가 int64 접근 방식에 너무 많은 기계를 투입한다는 것을 확인시켜줍니다.VC는 x86에서도 동일하게 수행합니다.그러나 x64의 경우 int64 접근법은 실제로 하나의 명령어보다 짧습니다(프로모션과 64비트 비교는 사소한 것이기 때문에).하지만 파이프라인이 실제 공연을 어느 쪽이든 가능하게 할 수도 있습니다.https://godbolt.org/g/wyG4yC
이것은 사안별로 판단해야 합니다.서명된 형식이 프로그램에 사용되는 이유는 여러 가지가 있습니다.
- 왜냐하면 실제로 계산이나 출력에서 음수가 나와야 하기 때문입니다.
- 은 가 "Sloppy typing"만 하며, 합니다.
int
그들의 프로그램 전체에 많은 생각을 하지 않고 있습니다. - 실수로 서명했습니다.된 숫자들은 된 숫자들을 만,다와 로 부호화된 되었습니다.
0
은, ㅇint
.
1)의 경우에는 서명된 산술로 연산을 수행해야 합니다.그런 다음 최대 기대 값을 포함하는 데 필요한 최소 유형으로 변환해야 합니다.
를 들어 어떤 이 , 합니다의 수 합니다.-10000
.10000
해야 합니다 그런 다음 16비트 서명된 유형을 사용하여 이를 표현해야 합니다.에 구애받지 않고할 수 있는 다입니다.int_fast16_t
.
int_fastn_t
그리고.uint_fastn_t
type들은 type이 적어도 n만큼 클 것을 요구하지만 컴파일러가 더 빠른 code/better alignment를 제공한다면 더 큰 type을 선택할 수 있습니다.
2) 됩니다를 됩니다.stdint.h
게으르지 않게 말입니다.프로그래머로서 프로그램에 선언된 모든 변수의 크기와 부호성을 항상 고려해야 합니다.이것은 선언 시점에서 이루어져야 합니다.아니면 나중에 어떤 형태로든 알게 되면 다시 돌아가서 유형을 바꾸세요.
만약 여러분이 그 종류들을 신중하게 고려하지 않는다면, 여러분은 틀림없이 수많은, 종종 미묘한 버그들을 쓰게 될 것입니다.이것은 C보다 타입 정확도에 대해 더 까다로운 C++에서 특히 중요합니다.
"slopy typing"이 사용될 경우, 실제 의도된 유형은 서명된 것이 아니라 서명되지 않은 것이 대부분입니다.다음과 같은 엉성한 타이핑 예를 생각해 보십시오.
for(int i=0; i<n; i++)
서명한 것을 사용한다는 것은 전혀 말이 안 되는데, 왜 그러십니까?다입니다.size_t
.
, 크기를 ,n
예를 들어, 100개를 보유할 수 있으며, 이에 가장 적합한 유형을 사용할 수 있습니다.
for(uint_fast8_t i=0; i<100; i++)
3) 또한 공부를 해서 치료됩니다.특히 이러한 언어들에 존재하는 암묵적인 승격을 위한 다양한 규칙들, 예를 들어 일반적인 산술 변환과 정수 승격.
두 개의 가지 버전은 확실히 느리겠지만 사실 그 중 어느 것도 두 개의 가지가 아닙니다.하나의 가지도...만약 당신이 부울 정수 결과를 원한다면 x86.
예를 들어, C++ 소스의 경우 x86-64 gcc 7.1 will:
bool compare(int x, unsigned int y) {
return (x < y); // "wrong" (will emit warning)
}
bool compare2(int x, unsigned int y) {
return (x < 0 || static_cast<unsigned int>(x) < y);
}
bool compare3(int x, unsigned int y) {
return static_cast<long long>(x) < static_cast<long long>(y);
}
이 어셈블리(godbolt live demo)를 제작합니다.(EDI 및 ESI의 알갱이, 상반부에 쓰레기가 있을 가능성이 있음)
compare(int, unsigned int):
cmp edi, esi
setb al
ret
compare2(int, unsigned int):
mov edx, edi
shr edx, 31 # sign bit of x
cmp edi, esi
setb al
or eax, edx
ret
compare3(int, unsigned int):
movsx rdi, edi # sign-extend x
mov esi, esi # zero-extend y
cmp rdi, rsi
setl al
ret
또한 복잡한 코드 내부에서 이러한 코드를 사용하려고 하면 99%의 경우(적어도 GCC는 32비트 레지스터에 값을 쓴 것을 이미 알고 있기 때문에 확장이 최적화되어 절반을 암시적으로 제로화할 수 있음) 인라인 처리됩니다.
프로파일링을 하지 않고서는 그냥 추측일 뿐이지만, "어쩌면" 저는 그와 함께 갈 것입니다.compare3
일부 되지 않을 r"서의 ,때의 정렬는 uint한 32->64면, 상위 32b에 있는 일부 엉망인 코드 호출과 비교되는 코드 호출을 생성하는 데는 상당한 노력이 필요할 것입니다.esi
더을 그으면 것에 ... 그러나 그것은 더 복잡한 계산에서 인라인될 때 아마도 그것을 제거할 것입니다. 거기서 그것은 또한 주장이 이미 unint64 확장되었다는 것에 주목할 것입니다.compare3
이 경우 더욱 간단해집니다(+ shorter.
를 들어, 알 수 할 수 C 코멘트에서 말했듯이, 저는 이 작업을 필요로 하는 작업에 부딪히지 않습니다. 예를 들어, 유효한 데이터 범위를 알 수 없는 작업에 대해 작업할 수 없습니다. 따라서 C/C++에서 작업하는 작업은 완벽하게 적합하고 작업 방식에 대해 감사합니다.<
signed vs unsigned type은 잘 정의되어 있으며 최단/fastest 코드를 생성하며, 프로그래머인 나로 하여금 이를 검증하고 소스를 적절하게 변경할 필요가 있는 경우 경고가 표시됩니다.
제시한 특정 설정을 고려하면 다음과 같습니다.
int x;
unsigned int y;
의 하려는 당신의 .x
.y
x
, 저는 그것을 다음과 같이 쓰고 싶습니다.
if ((x < 0) || (x < y)) {}
당신의 두번째 대안입니다.하고,한 더 합니다.y
의의 .x
의 타입. 를 갖도록 쓸.따라서, 만약 여러분이 논쟁이 그러한 형태를 가질 것이라고 기꺼이 규정한다면, 여러분은 그것을 여러분의 눈을 피해, C++ 지지자들, 즉 매크로로 쓸 수도 있습니다.
두 인수를 모두 서명된 64비트 정수형으로 변환하는 것은 휴대용 솔루션이 아닙니다. 왜냐하면 실제로 둘 중 하나에서 승격된다는 보장이 없기 때문입니다.int
아니면unsigned int
더 할 수 또한 더 넓은 유형으로 확장할 수 없습니다.
두 대안의 상대적 성능에 관해서는 큰 차이가 있을지 의문이지만, 문제가 중요하다면 신중한 벤치마크를 작성하는 것이 좋을 것입니다.저는 휴대용 대안이 다른 대안보다 하나의 기계 명령을 더 필요로 하는 것을 상상할 수 있고, 또한 하나를 덜 필요로 하는 것도 상상할 수 있습니다.이러한 비교가 애플리케이션의 성능을 지배하는 경우에만 하나의 명령이 어떤 방식으로든 눈에 띄는 차이를 가져올 것입니다.
물론, 이것은 당신이 제시한 상황에 맞는 것입니다.컴파일 시간에 정렬된 여러 가지 유형에 대해 혼합된 서명/비부호 비교를 순서대로 처리하려면 템플릿 기반 래퍼가 도움이 될 수 있지만, 비교 자체에 대한 세부 사항을 구체적으로 묻습니다.
당신이 할 수 있는 한 가지 휴대용 트릭은 당신이 두 인수를 다음으로 넓힐 수 있는지 확인하는 것입니다.intmax_t
<stdint.h>
통합형인 합니다를 하실 수 있습니다.(sizeof(intmax_t) > sizeof(x) && sizeof(intmax_t) >= sizeof(y))
만약 그렇다면, 확대 변환을 해보세요.에 작동합니다.int
,이는 32비트입니다.long long int
너비가 64비트입니다.
에서는 C++ 에서는를 한 비교 을 할 수 .std::numeric_limits<T>
그 주장에 대하여여기 한가지 버전이 있습니다. (컴파일 포함)-Wno-sign-compare
gcc or clang!)
#include <cassert>
#include <cstdint>
#include <limits>
using std::intmax_t;
using std::uintmax_t;
template<typename T, typename U>
inline bool safe_gt( T x, U y ) {
constexpr auto tinfo = std::numeric_limits<T>();
constexpr auto uinfo = std::numeric_limits<U>();
constexpr auto maxinfo = std::numeric_limits<intmax_t>();
static_assert(tinfo.is_integer, "");
static_assert(uinfo.is_integer, "");
if ( tinfo.is_signed == uinfo.is_signed )
return x > y;
else if ( maxinfo.max() >= tinfo.max() &&
maxinfo.max() >= uinfo.max() )
return static_cast<intmax_t>(x) > static_cast<intmax_t>(y);
else if (tinfo.is_signed) // x is signed, y unsigned.
return x > 0 && x > y;
else // y is signed, x unsigned.
return y < 0 || x > y;
}
int main()
{
assert(-2 > 1U);
assert(!safe_gt(-2, 1U));
assert(safe_gt(1U, -2));
assert(safe_gt(1UL, -2L));
assert(safe_gt(1ULL, -2LL));
assert(safe_gt(1ULL, -2));
}
두 개의 선을 바꾸면 부동소수점을 알 수 있습니다.
템플릿 지그재그 포켓으로 모든 시나리오에서 최적의 결과를 자동으로 얻을 수 있다고 생각합니다.
#include<iostream>
#include<cassert>
template<class T> auto make_unsigned(T i) -> T { return i; }
auto make_unsigned(int i) -> unsigned int {
assert(i >= 0);
return static_cast<unsigned int>(i);
}
auto make_unsigned(short i) -> unsigned short {
assert(i >= 0);
return static_cast<unsigned short>(i);
}
auto make_unsigned(long long i) -> unsigned long long {
assert(i >= 0);
return static_cast<unsigned long long>(i);
}
template<
class I1,
class I2,
std::enable_if_t<(std::is_signed<I1>::value and std::is_signed<I2>::value)
or (not std::is_signed<I1>::value and not std::is_signed<I2>::value)>* = nullptr
>
bool unsigned_less(I1 i1, I2 i2) {
return i1 < i2;
};
template<
class I1,
class I2,
std::enable_if_t<std::is_signed<I1>::value and not std::is_signed<I2>::value>* = nullptr
>
bool unsigned_less(I1 i1, I2 i2) {
return (i1 < 0) or make_unsigned(i1) < i2;
};
template<
class I1,
class I2,
std::enable_if_t<not std::is_signed<I1>::value and std::is_signed<I2>::value>* = nullptr
>
bool unsigned_less(I1 i1, I2 i2) {
return not (i2 < 0) and i1 < make_unsigned(i2);
};
int main() {
short a = 1;
unsigned int b = 2;
std::cout << unsigned_less(a, b) << std::endl;
using uint = unsigned int;
using ushort = unsigned short;
std::cout << unsigned_less(ushort(1), int(3)) << std::endl;
std::cout << unsigned_less(int(-1), uint(0)) << std::endl;
std::cout << unsigned_less(int(1), uint(0)) << std::endl;
return 0;
}
최근 베를린에서 열린 디자인 바이 인트로스펙션(Design by Introspection)에 관한 D 컨퍼런스에서 안드레이 알렉산드레수스(Andrei Alexandrescus)의 기조연설을 살펴보세요.
그 책에서 그는 DESIGN 시간에 체크 인트 클래스를 설계하는 방법을 보여주고 있는데, 그가 생각해낸 특징 중 하나는 바로 이것입니다. 바로 서명된 것과 서명되지 않은 것을 비교하는 방법입니다.
기본적으로 두 개의 비교를 수행해야 합니다.
(signed_var < 0)인 경우 unsigned_var를 반환합니다. 그렇지 않으면 signed_var를 unsigned_var로 승격/캐스팅한 다음 비교합니다.
언급URL : https://stackoverflow.com/questions/44068900/correct-unsigned-integer-comparison
'programing' 카테고리의 다른 글
Angular 간의 코드 재사용 방법JS 클라이언트 및 Node.js 서버 (0) | 2023.10.10 |
---|---|
수업이나 사전을 사용해야 합니까? (0) | 2023.10.10 |
How to export a data base from DataGrip at one SQL file? (0) | 2023.10.10 |
스프링 시큐리티 원형 콩 의존성 (0) | 2023.10.10 |
TSQL에서 테이블 이름 앞에 있는 "#"의 의미는 무엇입니까? (0) | 2023.10.10 |