이것은 탐구적인 질문입니다. 즉, 이 질문이 무엇에 관한 것인지 완전히 확신할 수는 없지만 Bash의 최대 정수와 관련이 있다고 생각합니다. 어쨌든 명확하게 정의하겠습니다.
$ echo $((1<<8))
256
조금 움직여 정수를 생성합니다. 얼마나 멀리 갈 수 있나요?
$ echo $((1<<80000))
1
분명히 우리는 아직 이 지점에 도달하지 않았습니다. (1은 예상치 못한 일이었습니다. 다시 설명하겠습니다.) 그러나,
$ echo $((1<<1022))
4611686018427387904
여전히 긍정적입니다. 그러나 이것은 아닙니다:
$ echo $((1<<1023))
-9223372036854775808
그리고 더 나아가,
$ echo $((1<<1024))
1
왜 1입니까?왜 다음과 같은 상황이 발생합니까?
$ echo $((1<<1025))
2
$ echo $((1<<1026))
4
이 시리즈를 분석하고 싶은 사람이 있나요?
고쳐 쓰다
내 컴퓨터:
$ uname -a
Linux tomas-Latitude-E4200 4.4.0-47-generic #68-Ubuntu SMP Wed Oct 26 19:39:52 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
답변1
불다변수를 사용하여 intmax_t
산술 연산 수행. 귀하의 시스템에서는 길이가 64비트이므로 다음과 같습니다.
$ echo $((1<<62))
4611686018427387904
이것은
100000000000000000000000000000000000000000000000000000000000000
이진 형식(1 뒤에 62개의 0이 옴). 다시 이동:
$ echo $((1<<63))
-9223372036854775808
이것은
1000000000000000000000000000000000000000000000000000000000000000
이진수(0 63개), 2의 보수 산술.
표현 가능한 가장 큰 정수를 얻으려면 1을 뺍니다.
$ echo $(((1<<63)-1))
9223372036854775807
이것은
111111111111111111111111111111111111111111111111111111111111111
바이너리 형태로.
지적한대로이르카초~의답변, 오프셋 모듈로 64를 사용하여 64비트로 이동x86CPU(사용 여부에 관계없이 RCL
) SHL
는 다음과 같이 나타나는 동작을 설명합니다.
$ echo $((1<<64))
1
와 동일 합니다 $((1<<0))
.$((1<<1025))
$((1<<1))
$((1<<1026))
$((1<<2))
유형 정의와 최대값은 다음에서 확인할 수 있습니다.stdint.h
;시스템에서:
/* Largest integral types. */
#if __WORDSIZE == 64
typedef long int intmax_t;
typedef unsigned long int uintmax_t;
#else
__extension__
typedef long long int intmax_t;
__extension__
typedef unsigned long long int uintmax_t;
#endif
/* Minimum for largest signed integral type. */
# define INTMAX_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum for largest signed integral type. */
# define INTMAX_MAX (__INT64_C(9223372036854775807))
답변2
2.05b의 CHANGES
문서 bash
:
제이. 이제 쉘은 long 대신 시스템이 지원하는 최대 정수 크기(intmax_t)를 사용하여 산술을 수행합니다.
x86_64 시스템의 intmax_t
부호 있는 64비트 정수에 해당합니다 . 그러면 -2^63
와 사이의 의미 있는 값을 얻게 됩니다 2^63-1
. 그 범위를 넘어서면 서라운드를 얻을 수 있습니다.
답변3
1024를 이동하면 이동량이 실제로 비트 수(64)의 모듈로이므로 1이 1024 === 64 === 0
됩니다 1025 === 65 === 1
.
a 이외의 값을 이동하면 1
이것이 비트 회전이 아님을 분명히 알 수 있습니다. 왜냐하면 이동 값이 (적어도) 64가 될 때까지 높은 비트가 낮은 비트로 순환되지 않기 때문입니다.
$ printf "%x\n" $(( 5 << 63 )) $(( 5 << 64 ))
8000000000000000
5
이 동작은 시스템에 따라 다를 수 있습니다. 이것bash 코드 Stephen이 링크됨오른쪽 값을 확인하지 않고 단순 시프트만 표시됩니다. 내 기억이 정확하다면 x86 프로세서는 시프트 값의 하위 6비트(64비트 모드)만 사용하므로 이 동작은 기계어에서 직접 나타날 수 있습니다. 또한 C에서는 비트 폭을 넘어서는 이동이 명확하게 정의되어 있지 않다고 생각합니다( gcc
이 점에 대해 경고).
답변4
이동하여 정수를 생성합니다. 얼마나 멀리 갈 수 있나요?
최대 정수는 래핑을 나타냅니다(대부분의 쉘에서 기본값).
64비트 정수는 일반적으로 2**63 - 1
.
그게 0x7fffffffffffffff
아니면 9223372036854775807
12월이에요.
숫자 "+1"은 음수가 됩니다.
이는 와 동일하므로 다음과 같습니다 1<<63
.
$ echo "$((1<<62)) $((1<<63)) and $((1<<64))"
4611686018427387904 -9223372036854775808 and 1
그 후 프로세스가 다시 반복됩니다.
$((1<<80000)) $((1<<1022)) $((1<<1023)) $((1<<1024)) $((1<<1025)) $((1<<1026))
결과는 mod 64
이동 값 [a] 에 따라 달라집니다 .
[1] 보낸 사람:인텔® 64 및 IA-32 아키텍처 소프트웨어 개발자 핸드북: 볼륨 2개수는 5비트(또는 64비트 모드에서 REX.W를 사용하는 경우 6비트)로 마스크됩니다. 카운트 범위는 0~31(또는 64비트 모드를 사용하고 REX.W를 사용하는 경우 63)로 제한됩니다. .
또한: $((1<<0))
그것이다는 것을 기억하십시오1
$ for i in 80000 1022 1023 1024 1025 1026; do echo "$((i%64)) $((1<<i))"; done
0 1
62 4611686018427387904
63 -9223372036854775808
0 1
1 2
2 4
따라서 그것은 모두 숫자가 64의 배수에 얼마나 가까운지에 달려 있습니다.
테스트 한계:
가장 큰 양수(및 음수) 정수를 테스트하는 신뢰할 수 있는 방법은 각 비트를 차례로 테스트하는 것입니다. 어쨌든 대부분의 컴퓨터에서는 64단계 미만이고 너무 느리지도 않습니다.
세게 때리다
먼저, 형식의 가장 큰 정수가 필요합니다 2^n
(1비트 뒤에 0이 오는 집합). 우리는 왼쪽으로 이동하여 이를 수행할 수 있습니다.다음Shift를 사용하면 숫자가 음수가 됩니다("줄바꿈"이라고도 함).
a=1; while ((a>0)); do ((b=a,a<<=1)) ; done
결과는 다음과 같습니다 b
. 루프가 실패한 마지막 교대 이전의 값입니다.
그런 다음 각 비트를 시도하여 어느 것이 부호에 영향을 미치는지 알아내야 합니다 e
.
c=$b;d=$b;
while ((c>>=1)); do
((e=d+c))
(( e>0 )) && ((d=e))
done;
intmax=$d
가장 큰 정수( intmax
)는 의 마지막 값에서 파생됩니다 d
.
부정적인 측면(보다 작음 0
)에서는 모든 테스트를 반복하지만, 비트를 래핑하지 않고 0으로 설정할 수 있을 때 테스트합니다.
모든 단계를 인쇄하는 전체 테스트는 다음과 같습니다(bash의 경우).
#!/bin/bash
sayit(){ printf '%020d 0x%016x\n' "$1"{,}; }
a=1; while ((a>0)) ; do((b=a,a<<=1)) ; sayit "$a"; done
c=$b;d=$b; while((c>>=1)); do((e=d+c));((e>0))&&((d=e)) ; sayit "$d"; done;
intmax=$d
a=-1; while ((a<0)) ; do((b=a,a<<=1)) ; sayit "$b"; done;
c=$b;d=$b; while ((c<-1)); do((c>>=1,e=d+c));((e<0))&&((d=e)); sayit "$d"; done
intmin=$d
printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"
쉿
거의 모든 쉘로 변환됩니다.
#!/bin/sh
printing=false
sayit(){ "$printing" && printf '%020d 0x%016x\n' "$1" "$1"; }
a=1; while [ "$a" -gt 0 ];do b=$a;a=$((a<<1)); sayit "$a"; done
c=$b;d=$b; while c=$((c>>1)); [ "$c" -gt 0 ];do e=$((d+c)); [ "$e" -gt 0 ] && d=$e ; sayit "$d"; done;
intmax=$d
a=-1; while [ "$a" -lt 0 ];do b=$a;a=$((a<<1)); sayit "$b"; done;
c=$b;d=$b; while [ "$c" -lt -1 ];do c=$((c>>1));e=$((d+c));[ "$e" -lt 0 ] && d=$e ; sayit "$d"; done
intmin=$d
printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"
많은 쉘에 대해 위 명령을 실행하면 모두(bash 2.04 및 mksh 제외)는 이 시스템에서
최대 ( ) 값을 허용합니다 .2**63 -1
흥미롭게도 보고서는 다음과 같이 말합니다.att 쉘:
$ attsh --version
version sh (AT&T Research) 93u+ 2012-08-01
그러나 오류는 ksh가 아닌 ksh 값에 인쇄됩니다 $((2^63))
.