Існують обмеження, встановлені для можливостей оцінки арифметичної 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, є правильною
- (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) + 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 в цьому сенсі (я можу додати вгору на папері або використовувати його в bc). Крім того, поведінка схожа на поведінку 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
.