Существуют ограничения, установленные для возможностей арифметической оценки 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
.Ответы:
Нет. Как вы это понимаете? По вашему собственному примеру, максимум это:
Если «переполнение» означало, что «вы получаете отрицательные целые числа, показывающие, как далеко вы находитесь от ULONG_MAX», то если мы добавим одно к этому, разве мы не получим -1? Но вместо этого:
Возможно, вы имеете в виду, что это число, к которому вы можете добавить
$max
отрицательную разницу, так как:Но на самом деле это не так:
Это потому, что система использует два дополнения для реализации целых чисел со знаком. 1 Значение, полученное в результате переполнения , НЕ является попыткой предоставить вам разницу, отрицательную разницу и т. Д. Это буквально является результатом усечения значения до ограниченного числа битов, а затем его интерпретации как целое число со знаком, дополняющим два. , Например, причина
$(($max + 1 + $max))
возникает как -1, потому что самое высокое значение в дополнении к двум - это все установленные биты, кроме самого высокого бита (который указывает на отрицательное значение); сложить их вместе в основном означает перенести все биты влево, чтобы вы получили (если размер был 16-битным, а не 64):Старший бит (знак) теперь установлен, потому что он переносится в дополнении. Если вы добавите еще один (00000000 00000001) к этому, тогда у вас будут установлены все биты , которые в дополнении к двум равны -1.
Я думаю, что это частично отвечает на вторую половину вашего первого вопроса - «Почему отрицательные целые числа ... выставляются конечному пользователю?». Во-первых, потому что это правильное значение в соответствии с правилами 64-битных чисел с дополнением до двух. Это обычная практика большинства (других) языков программирования высокого уровня общего назначения (я не могу придумать тот, который этого не делает), поэтому
bash
придерживается соглашения. Что также является ответом на первую часть первого вопроса - «Каково обоснование?»: Это норма в спецификации языков программирования.WRT 2-й вопрос, я не слышал о системах, которые интерактивно меняют ULONG_MAX.
Это не будет иметь никакого значения для того, как получается арифметика, потому что это не произвольное значение, которое используется для конфигурации системы - это удобное значение, которое хранит неизменную константу, отражающую аппаратное обеспечение. По аналогии, вы могли бы переопределить c как 55 миль в час, но скорость света все равно будет 186 000 миль в секунду. c - это не число, используемое для настройки вселенной, а вывод о природе вселенной.
ULONG_MAX точно такой же. Он выводится / рассчитывается на основе характера N-разрядных чисел. Изменение его
limits.h
было бы очень плохой идеей, если бы эта константа использовалась где-то, предполагая, что она должна представлять реальность системы .И вы не можете изменить реальность, навязанную вашим оборудованием.
1. Я не думаю, что это (средство целочисленного представления) на самом деле гарантируется
bash
, поскольку это зависит от базовой библиотеки C, а стандарт C не гарантирует этого. Тем не менее, это то, что используется на большинстве обычных современных компьютеров.источник
$max
, как вы описываете. Мои соображения: 1) это не цель, 2) убедитесь, что вы понимаете, хотите ли вы это сделать, 3) это не очень полезно из-за очень ограниченной применимости, 4) в соответствии со сноской, фактически не гарантируется, что система использовать два дополнения. Короче говоря, пытаться использовать это в программном коде было бы очень плохой практикой. Существуют библиотеки / модули с «большим числом» (для оболочек под POSIX,bc
) - используйте их, если вам нужно.