Существуют ограничения, установленные для возможностей арифметической оценки bash
оболочки. Руководство кратко об этом аспекте арифметики оболочки, но гласит :
Оценка выполняется в целых числах фиксированной ширины без проверки переполнения, хотя деление на 0 перехватывается и помечается как ошибка. Операторы и их приоритет, ассоциативность и значения такие же, как в языке Си.
К какому целому числу фиксированной ширины это относится, на самом деле зависит, какой тип данных используется (и особенности того, почему это не так), но предельное значение выражается /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
Таким образом, между 2 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 выставляются конечному пользователю, когда он что-то оценивает? - Я где-то читал, что какой-то вкус UNIX может интерактивно изменить ULONG_MAX? Кто-нибудь слышал об этом?
- Если кто-то произвольно изменяет значение максимума целого без знака в
limits.h
, а затем перекомпилируетbash
, что мы можем ожидать, произойдет?
Запись
1. Я хотел более четко проиллюстрировать увиденное, поскольку это очень простой эмпирический материал. Что я заметил, так это:
- (а) Любая оценка, которая дает <2 ^ 63-1, является правильной
- (б) Любая оценка, которая дает => 2 ^ 63 до 2 ^ 64, дает отрицательное целое число:
- Диапазон этого целого числа от x до y. х = -9223372036854775808 и у = 0.
Учитывая это, оценка, подобная (b), может быть выражена как 2 ^ 63-1 плюс что-то в пределах x..y. Например, если нас буквально просят оценить (2 ^ 63-1) +100 002 (но может быть любое число меньше, чем в (a)), мы получим -9223372036854675807. Я просто констатирую очевидное, я думаю, но это также означает, что два следующих выражения:
- (2 ^ 63-1) + 100 002 А;
- (2 ^ 63-1) + (LLONG_MAX - {что нам дает оболочка ((2 ^ 63-1) + 100 002), что составляет -9223372036854675807}), используя положительные значения, которые мы имеем;
- (2 ^ 63-1) + (9223372036854775807 - 9223372036854675807 = 100 000)
- = 9223372036854775807 + 100 000
действительно очень близки Второе выражение равно «2», кроме (2 ^ 63-1) + 100 002, то есть того, что мы оцениваем. Это то, что я имею в виду, когда вы получаете отрицательные целые числа, показывающие, насколько вы далеки от 2 ^ 64. Я имею в виду, что с этими отрицательными целыми числами и знанием пределов, вы не можете закончить оценку в диапазоне x..y в оболочке bash, но вы можете в другом месте - в этом смысле данные можно использовать до 2 ^ 64 (я мог бы добавить это на бумаге или использовать его в до н.э). Помимо этого, однако, поведение аналогично 6 ^ 6 ^ 6, так как предел достигнут, как описано ниже в Q ...
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)
.
bc
, установив BC_LINE_LENGTH=0
.