bash
쉘 의 산술 평가 기능에 대한 한계가 설정되어 있습니다. 이 매뉴얼은 쉘 산술 의이 측면에 대해 간결하지만 상태는 다음과 같습니다.
오버플로를 확인하지 않고 고정 폭 정수로 평가를 수행하지만 0으로 나누기가 트래핑되어 오류로 표시됩니다. 연산자와 그 우선 순위, 연관성 및 값은 C 언어와 동일합니다.
이것이 말하는 고정 너비 정수는 실제로 어떤 데이터 유형 이 사용 되는지에 관한 것입니다 (그리고 이것이 이것을 초과하는 이유에 대한 세부 사항). 한계 값은 다음 /usr/include/limits.h
과 같은 방식으로 표현됩니다 :
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
그리고 일단 당신이 그것을 알면, 당신은 이렇게 사실을 확인할 수 있습니다 :
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
이것은 64 비트 정수 이며 산술 평가와 관련하여 쉘에서 직접 변환됩니다.
# echo $(((2**63)-1)); echo $((2**63)); echo $(((2**63)+1)); echo $((2**64))
9223372036854775807 //the practical usable limit for your everyday use
-9223372036854775808 //you're that much "away" from 2^64
-9223372036854775807
0
# echo $((9223372036854775808+9223372036854775807))
-1
그래서 둘 사이에 63 2 64 -1, 당신은 얼마나 당신이 ULONG_MAX에서 떨어져을 보여주는 부정적인 정수를 얻을 1 . 평가가 한계에 도달하고 순서에 상관없이 경고가 표시되지 않고 평가의 일부가 0으로 재설정되어 오른쪽 연관 지수 와 같은 비정상적인 동작이 발생할 수 있습니다 .
echo $((6**6**6)) 0 // 6^46656 overflows to 0
echo $((6**6**6**6)) 1 // 6^(6^46656) = 6^0 = 1
echo $((6**6**6**6**6)) 6 // 6^(6(6^46656)) = 6^(6^0) = 6^1
echo $((6**6**6**6**6**6)) 46656 // 6^(6^(6^(6^46656))) = 6^6
echo $((6**6**6**6**6**6**6)) 0 // = 6^6^6^1 = 0
...
를 사용하면 sh -c 'command'
아무것도 변경되지 않으므로 이것이 정상적이고 호환되는 출력이라고 가정해야합니다. 식 범위와 한계에 대한 기본적이지만 구체적인 이해와 표현식 평가를 위해 쉘에서 의미하는 바를 이해 했으므로 Linux의 다른 소프트웨어가 사용하는 데이터 유형을 빠르게 볼 수 있다고 생각했습니다. bash
이 명령의 입력을 보완하기 위해 몇 가지 소스를 사용했습니다 .
{ shopt -s globstar; for i in /path/to/source_bash-4.2/include/**/*.h /usr/include/**/*.h; do grep -HE '\b(([UL])|(UL)|())LONG|\bFLOAT|\bDOUBLE|\bINT' $i; done; } | grep -iE 'bash.*max'
bash-4.2/include/typemax.h:# define LLONG_MAX TYPE_MAXIMUM(long long int)
bash-4.2/include/typemax.h:# define ULLONG_MAX TYPE_MAXIMUM(unsigned long long int)
bash-4.2/include/typemax.h:# define INT_MAX TYPE_MAXIMUM(int)
if
문에 더 많은 출력이 있으며 awk
등 의 명령을 검색 할 수 있습니다. 사용한 정규 표현식이 bc
및과 같은 임의의 정밀 도구에 대해 아무것도 포착하지 못합니다 dc
.
질문
awk
산술 평가가 오버플로 될 때 경고하지 않는 이유는 무엇입니까 ( 2 ^ 1024를 평가할 때 와 같음 )? 2 63 에서 2 64 -1 사이의 음의 정수가 왜 무언가를 평가할 때 최종 사용자에게 노출됩니까?- 유닉스의 맛이 대화식으로 ULONG_MAX를 변경할 수 있다는 것을 읽었습니다. 아무도 들어 본 적이 있습니까?
- 누군가가 부호없는 정수 최대 값을 임의로 변경
limits.h
한 다음 다시 컴파일bash
하면 어떻게 될까요?
노트
1. 나는 매우 간단한 경험적인 것들이기 때문에 내가 본 것을 더 명확하게 설명하고 싶었다. 내가 알아 차린 것은 :
- (a) <2 ^ 63-1을 제공하는 모든 평가는 정확합니다
- (b) => 2 ^ 63에서 최대 2 ^ 64까지의 평가는 음의 정수를 나타냅니다.
- 해당 정수의 범위는 x에서 y입니다. x = -9223372036854775808 및 y = 0입니다.
이를 고려하면 (b)와 같은 평가는 2 ^ 63-1 + x..y 내의 어떤 것으로 표현할 수 있습니다. 예를 들어 문자 그대로 (2 ^ 63-1) +100 002 ((a)보다 작은 숫자 일 수 있음)를 평가하라는 요청을 받으면 -9223372036854675807이됩니다. 나는 단지 내가 추측 한 명백한 것을 진술하고 있지만 이것은 또한 다음 두 가지 표현을 의미합니다.
- (2 ^ 63-1) + 100002 AND;
- (2 ^ 63-1) + (LLONG_MAX-{쉘이 ((2 ^ 63-1) + 100 002)에 대해 -9223372036854675807}을 제공하는 것) 우리가 가진 양수 값을 사용하여 잘;
- (2 ^ 63-1) + (9223372036854775807-9223372036854675807 = 100 000)
- = 9223372036854775807 + 100 000
실제로 매우 가깝습니다. 두 번째 표현은 (2 ^ 63-1) + 100 002, 즉 우리가 평가하고있는 것 외에 "2"입니다. 이것은 내가 2 ^ 64에서 얼마나 떨어져 있는지 보여주는 음의 정수를 얻는다는 것을 의미합니다. 음의 정수와 한계에 대한 지식으로 bash 쉘의 x..y 범위 내에서 평가를 완료 할 수는 없지만 다른 곳에서는 가능합니다. 데이터는 그런 의미에서 최대 2 ^ 64까지 사용할 수 있습니다 (추가 가능 종이에 올리거나 bc에서 사용하십시오). 그러나 Q에서 아래에 설명 된 것처럼 한계에 도달하면 동작은 6 ^ 6 ^ 6의 동작과 비슷합니다.
bc
하십시오 $num=$(echo 6^6^6 | bc)
. 불행히도 bc
줄 바꿈을하기 때문에 num=$(echo $num | sed 's/\\\s//g')
나중에 해야합니다 . 파이프에서 작업하면 실제로 줄 바꿈 문자가 있으며 sed에는 어색하지만 num=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
작동합니다. 두 경우 모두 이제 사용할 수있는 정수가 있습니다 (예 :) num2=$(echo "$num * 2" | bc)
.