Bash 산술이 처리할 수 있는 가장 큰 숫자를 찾으십니까?

Bash 산술이 처리할 수 있는 가장 큰 숫자를 찾으십니까?

내 스크립트가 자체적으로 최대 수를 결정하도록 하려면 어떻게 해야 합니까?

내 환경 변수를 살펴보고 유망해 보이는 다음 두 가지를 찾았습니다.

~# declare -p BASH_VERSINFO HOSTTYPE
declare -ar BASH_VERSINFO=([0]="5" [1]="0" [2]="11" [3]="1" [4]="release" [5]="x86_64-slackware-linux-gnu")
declare -- HOSTTYPE="x86_64"

...하지만 Bash 산술에서 가장 큰 숫자가 무엇인지 결론을 내리기 위해 이를 구문 분석할 수 있다고 정말로 신뢰할 수 있습니까? 프로그래밍 방식으로 더 나은 방법이 있어야 합니다. 어떤 제안이 있으십니까?

답변1

Bash 산술은 부호 있는 숫자를 사용합니다.

따라서 빠른 대답은 다음과 같습니다.

((MAX=(1<<63)-1))

그러나 스크립트가 실행 중인 시스템의 비트에 대해 불가지론적이기를 원하므로 계속 진행하겠습니다.

무차별 대입은 음수로 오버플로되는 지점에 도달할 때까지 루프에 1을 계속 추가하는 것입니다. 하지만 이는 몇 년이 걸릴 수도 있습니다! :-) 더 빠르고 우아한 방법은 간단한 비트 이동을 사용하는 것입니다.

부호 비트를 찾아봅시다. 즉, 1최상위 비트에서 숫자를 찾고, 개수에 상관없이 다른 모든 비트에서 0을 찾아보겠습니다. 이 숫자를 얻은 후에는 1숫자를 빼면 가장 큰 부호가 있는 숫자를 얻게 됩니다.

# MIN -- the smallest signed number 0x8000...00  (it equals MAX+1)
# MAX -- the largest signed number  0x7Fff...FF  <-- what we are looking for

MIN=1; until (( (MIN<<=1) < 0 )) ;do :;done
((MAX=MIN-1))

echo $MAX

Result:
9223372036854775807

또는 루프가 없는 단일 라인입니다. 숫자의 16진수 표현을 변수에 넣은 다음 이를 내장 함수에 전달할 때 변수 확장을 통해 부호 비트를 마스크합니다 printf.

printf -v MAX %x -1 && printf -v MAX %d 0x${MAX/f/7}

echo $MAX

Result:
9223372036854775807

나와 다른 자릿수를 가진 컴퓨터에서는 결과가 다른 숫자가 됩니다.

설명을 위해 제 경우에는 다음과 같습니다.

printf "MAX %X  %d\nMIN %X %d\n" $MAX $MAX $MIN $MIN
MAX 7FFFFFFFFFFFFFFF  9223372036854775807
MIN 8000000000000000 -9223372036854775808

MIN에 대한 약간의 참고 사항: MIN만 사용하도록 제한하고 싶을 수도 있습니다 ((MIN=-MAX)). 그렇지 않으면 일부 산술 연산에 가끔 문제가 발생할 수 있습니다.

((MIN=-MAX)) ; printf "MIN %X %d\n" $MIN $MIN
MIN 8000000000000001 -9223372036854775807

답변2

2단계 텍스트 조작 작업으로 최대량을 얻을 수 있습니다.

TL;박사:당신이 해야 할 bash일은 다음과 같습니다:

printf -v ff %x -1
printf -v max %d "0x${ff/#?/7}"

그리고 다음과 같은 추가 연산을 통해 최소값을 얻을 수 있습니다.

# this uses the shell's own arithmetic engine
min="$((max+1))"

OP의 의견에 답하려면 내장 명령으로 bash구현되었으며 printf기본적으로 내장 명령을 선호하므로 printf일반적으로 독립 실행형 실행 파일로 사용되는 외부 명령을 호출하려고 시도하지 않습니다 $PATH.

또한 위에서 설명한 대로 bash사용자 고유의 내장 함수를 사용하면 printf추가 프로세스가 생성되지 않습니다. 대신 변수가 생성 $ff되고 $max편리한 옵션으로 지정되는 셸의 현재 실행 환경에서 실행됩니다 -v.


위와 동일하지만 POSIX 호환 구문은 다음과 같습니다.

ff="$(printf %x -1)"
max="$(printf %d "0x7${ff#?}")"

# to obtain the minimum number in shells that only
# support integer numbers you can just do like
# said for `bash`
min="$((max+1))"

# else for shells defaulting to floating-point numbers
# (such as ksh93) you might instead do one text-manipulation
# operation on top of the arithmetic addition
min="$(printf -- -%u "$((max+1))")"
# of course such result would only apply to the shell's
# integer capacities, not to its floating-point capacities

위의 POSIX 호환 구문을 사용한다는 점에 유의하세요.가능한특정 쉘이 printf유사한 간단한 명령을 최적화하는지 여부에 따라 각 명령 대체에 대한 (임시) 프로세스를 생성합니다. 그러나 쉘이 이러한 각 프로세스에 대해 프로세스를 생성하더라도 printf단순히 자체적으로 분기하여 수행하므로 다른 임의의 쉘의 연산 기능이 아닌 자체 산술 기능에 여전히 의존합니다.

이는 쉘이 다음 printf과 같이 구현되었다고 가정합니다.내장, bash다른 많은 껍질과 마찬가지로. 내장으로 구현되지 않은 쉘은 사용 가능한 printf외부 명령(있는 경우)을 사용합니다. 이는 외부 명령, 운영 체제의 C 라이브러리 및 CPU 자체의 기능 측면에서 유효한 결과를 생성하지만 반드시 다음과 일치하지는 않을 수 있습니다.printf$PATHprintf쉘 자신의mksh64비트 시스템에서도 32비트 산술을 사용하는 등의 산술 기능 . 내장 명령으로 mksh구현되지 않을 수도 있고 , 외부 명령이 여전히 선호될 수도 있으며, 내장 명령 으로 컴파일되지 않은 경우 내장 명령으로 구현되지 않을 수도 있다는 점은 주목할 가치가 있습니다 .printfashbusyboxprintf

답변3

저는 64비트 컴퓨터에 앉아 있습니다. 그런데 스크립트가 스스로 이를 결정하도록 하려면 어떻게 해야 합니까?

이것은 bash 산술에서 가장 큰 숫자가 무엇인지와는 다른 질문입니다. 이를 사용하여 컴퓨터의 비트 수를 결정할 수 없으며 컴퓨터의 비트 수는 Bash 정수의 크기를 결정하지 않습니다.

Bash 번호는 32비트 시스템에서도 항상 64비트입니다. (또는 ISO C에서 요구하는 최소 64비트보다 넓은 이국적인 시스템에서는 더 넓을 수도 있습니다 long long.
min = -maxBash -max - 1가 논평자에 따르면 Bash는 2의 보수를 사용하지 않는 C 구현으로 이식 가능하므로 long long런타임 unsigned long long테스트 방법을 설계할 때 이 점을 염두에 두십시오. )


호기심에 Bash 3.2.39를 실행하는 오래된 32비트 Debian 시스템에서 이것을 테스트했습니다. LL3의 방법은 printf %x -116 f초(즉, 8바이트, 64비트)를 인쇄하는 것을 보여줍니다.

INT64_MAX를 INT64_MIN으로 늘려 bash 수학을 테스트했습니다.

$ uname -a
Linux <non-updated kernel version hidden to protect the guilty> i686 GNU/Linux

$ echo $((9223372036854775807 + 1))
-9223372036854775808

Bash의 소스 코드는 32비트 시스템에서 32비트와 같은 유형이 아닌 int64_tor long long(또는 uint64_t해당 동작을 정의하기 위해 구축되지 않은 한 래핑할 때 C의 정의되지 않은 동작을 피하고 싶어함)을 사용한다는 것이 분명합니다 .gcc -fwrapvlong

32비트 시스템에서 64비트 추가를 수행하면 컴파일러는 캐리 플래그가 있는 시스템에서 add+ (캐리 포함 추가) 또는 캐리 플래그가 없는 시스템에서 세 번째 명령어와 같은 2개의 명령어를 사용하게 됩니다. adc따라서 언어가 64비트 유형을 제공하는 것은 지극히 정상이며, 한 가지 유형만 있는 고급 언어의 경우 좋은 와이드 유형을 사용하는 것은 지극히 정상입니다.

관련 정보