Побитовый сдвиг и самое большое целое число в Bash

краткое: в вашем .tmux.conf

set -g visual-bell on

long: Некоторые приложения проверяют как bel ] и показывают возможности терминала и позволяют выбрать, какой из них будет использоваться. tmux не работает. Он читает только bel , поэтому вы можете изменить описание вашего терминала, изменив bel на строку для мигания дисплея.

Это может сработать, за исключением того, что, хотя tmux читает данные terminfo, он не использует соответствующие функции tputs или putp для отправки данных terminfo. что включает в себя задержки по времени. Вам нужны эти задержки на большинстве терминалов (включая все имитирующие xterm) для реализации flash .

Интересно, что в описании терминала для screen есть flash = \ Eg , но ни он, ни tmux ничего с этим не делают (screen использует его внутри как часть своей функции визуального звонка ). tmux также имеет функцию визуального звонка, которую вы можете установить в своем файле конфигурации.Это не приведет к миганию экрана, а просто поместит кратковременное предупреждение в строку состояния.

Дополнительная литература:

visual-bell [on | off]
Если эта опция включена, сообщение отображается в колокольчике, а не передается на терминал (который обычно издает звук). Также см. Опцию колокольчик .

колокольчик [любой | нет | текущий | другой]
Установить действие на колокольчике окна. любой означает, что звонок в любом окне, связанном с сеансом, вызывает звонок в текущем окне этого сеанса, none означает, что все колокола игнорируются, текущий означает, что игнорируются только колокольчики в окнах, отличных от текущего окна, а другое означает, что колокола в текущем окне игнорируются, но не в других окнах.

16
23.11.2016, 01:47
4 ответа

Bash использует переменные intmax_t для арифметики. В вашей системе они имеют длину 64 бита, поэтому:

$ echo $((1<<62))
4611686018427387904

- это

100000000000000000000000000000000000000000000000000000000000000

в двоичном формате (1, за которой следуют 62 0). Снова сдвиньте:

$ echo $((1<<63))
-9223372036854775808

что равно

1000000000000000000000000000000000000000000000000000000000000000

в двоичном формате (63 0), в арифметике с двумя дополнениями.

Чтобы получить самое большое представимое целое число, нужно вычесть 1:

$ echo $(((1<<63)-1))
9223372036854775807

что в двоичном исчислении равно

111111111111111111111111111111111111111111111111111111111111111

-.

Как указано в ilkkachu в ответе, на 64-битных x86 процессорах (будь то RCL или SHL) сдвиг происходит по модулю 64, что объясняет наблюдаемое вами поведение:

$ echo $((1<<64))
1

эквивалентно $((1<<0)). Таким образом, $((1<<1025)) - это $((1<<1)), $((1<<1026)) - это $((1<<2))...

Определения типов и максимальные значения вы найдете в stdint.h; в вашей системе:

/* Largest integral types.  */
#if __WORDSIZE == 64
typedef long int                intmax_t;
typedef unsigned long int       uintmax_t;
#else
__extension__
typedef long long int           intmax_t;
__extension__
typedef unsigned long long int  uintmax_t;
#endif

/* Minimum for largest signed integral type.  */
# define INTMAX_MIN             (-__INT64_C(9223372036854775807)-1)
/* Maximum for largest signed integral type.  */
# define INTMAX_MAX             (__INT64_C(9223372036854775807))
32
27.01.2020, 19:48

Сдвиг на 1024 дает единицу, потому что величина сдвига фактически берется по модулю количества бит (64), поэтому 1024 === 64 === 0 и 1025 === 65 === 1 .

Сдвиг чего-то другого, кроме 1 , проясняет, что это не вращение битов, так как старшие биты не переходят в нижний предел до того, как значение сдвига станет (как минимум) 64:

$ printf "%x\n" $(( 5 << 63 )) $(( 5 << 64 ))
8000000000000000
5

Возможно, это поведение зависит от системы. Bash-код , который Стивен связал с , показывает простой сдвиг, без какой-либо проверки правильного значения. Если я правильно помню, процессоры x86 используют только нижние шесть бит значения сдвига (в 64-битном режиме), поэтому поведение может быть прямо из машинного языка. Кроме того, я думаю, что сдвиги, превышающие разрядность, также четко не определены в C ( gcc предупреждает об этом).

4
27.01.2020, 19:48

Из файла CHANGES для bash 2.05b:

j. Оболочка теперь выполняет арифметику в наибольшем размере целого числа, который поддерживает машина (intmax_t). машина поддерживает (intmax_t), вместо long.

На машинах x86_64 intmax_t соответствует знаковым 64-битным целым числам. Таким образом, вы получите значимые значения между -2^63 и 2^63-1. За пределами этого диапазона вы просто получаете обходные пути.

5
27.01.2020, 19:48

дает целое число путем сдвига бита. Как далеко я могу зайти?

Пока целочисленное представление не завершится (по умолчанию в большинстве оболочек).
64-битное целое число обычно оборачивается вокруг 2 ** 63 - 1 .
Это 0x7ffffffffffffff или 9223372036854775807 в децил.

Это число «+1» становится отрицательным.

Это то же самое, что 1 << 63 , таким образом:

$ echo "$((1<<62)) $((1<<63)) and $((1<<64))"
4611686018427387904 -9223372036854775808 and 1

После этого процесс повторяется снова.

 $ ((1 << 80000)) $ ((1 << 1022)) $ ((1 << 1023)) $ ((1 << 1024)) $ ((1 << 1025)) $ ( (1 << 1026)) 
 

Результат зависит от mod 64 значения сдвига [a] .

[a] От: Руководство разработчика программного обеспечения для архитектур Intel® 64 и IA-32: Том 2 Счетчик замаскирован до 5 бит (или 6 бит, если в 64-битном режиме и используется REX.W). Диапазон счета ограничен от 0 до 31 (или 63, если используется 64-битный режим и REX.W). .

Также: помните, что $ ((1 << 0)) равно 1

$ for i in 80000 1022 1023 1024 1025 1026; do echo "$((i%64)) $((1<<i))"; done
 0 1
62 4611686018427387904
63 -9223372036854775808
 0 1
 1 2
 2 4

Итак, все зависит от того, насколько близко это число к числу, кратному 64.

Проверка предела:

Надежный способ проверки максимального положительного (и отрицательного) целого числа - проверить каждый бит по очереди. В любом случае для большинства компьютеров это менее 64 шагов, это не будет слишком медленным.

bash

Сначала нам нужно наибольшее целое число в форме 2 ^ n (1 бит, за которым следуют нули).Мы можем сделать это, сдвинувшись влево до тех пор, пока следующий сдвиг не сделает число отрицательным, что также называется «циклическим сдвигом»:

a=1;   while ((a>0));  do ((b=a,a<<=1))  ; done

Где b - результат: значение до последнего сдвига. это не цикл.

Затем нам нужно попробовать каждый бит, чтобы выяснить, какие из них влияют на знак e :

c=$b;d=$b;
while ((c>>=1)); do
      ((e=d+c))
      (( e>0 )) && ((d=e))
done;
intmax=$d

Максимальное целое число ( intmax ) получается из последнего значения ] d .

С отрицательной стороны (меньше, чем 0 ) мы повторяем все тесты, кроме тестирования, когда бит может быть равен 0 без повторения.

Полный тест с печатью всех шагов (для bash) выглядит так (для bash):

#!/bin/bash
sayit(){ printf '%020d 0x%016x\n' "$1"{,}; }
a=1;       while ((a>0)) ; do((b=a,a<<=1))              ; sayit "$a"; done
c=$b;d=$b; while((c>>=1)); do((e=d+c));((e>0))&&((d=e)) ; sayit "$d"; done;
intmax=$d
a=-1;      while ((a<0)) ; do((b=a,a<<=1))              ; sayit "$b"; done;
c=$b;d=$b; while ((c<-1)); do((c>>=1,e=d+c));((e<0))&&((d=e)); sayit "$d"; done
intmin=$d       

printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"

sh

Переведено почти для любой оболочки:

#!/bin/sh
printing=false
sayit(){ "$printing" && printf '%020d 0x%016x\n' "$1" "$1"; }
a=1;       while [ "$a" -gt 0  ];do b=$a;a=$((a<<1)); sayit "$a"; done
c=$b;d=$b; while c=$((c>>1)); [ "$c" -gt 0 ];do e=$((d+c)); [ "$e" -gt 0 ] && d=$e ; sayit "$d"; done;
intmax=$d
a=-1;      while [ "$a" -lt 0  ];do b=$a;a=$((a<<1)); sayit "$b"; done;
c=$b;d=$b; while [ "$c" -lt -1 ];do c=$((c>>1));e=$((d+c));[ "$e" -lt 0 ] && d=$e ; sayit "$d"; done
intmin=$d       

printf '%20d max positive value 0x%016x\n' "$intmax" "$intmax"
printf '%20d min negative value 0x%016x\n' "$intmin" "$intmin"

Выполнение вышеуказанного для многих оболочек,
все (кроме bash 2.04 и mksh) на этом компьютере принимаются значения до ( 2 ** 63 -1 ).

Интересно сообщить, что оболочка att :

$ attsh --version
version         sh (AT&T Research) 93u+ 2012-08-01

выдает ошибку при значениях $ ((2 ^ 63)) , но не ksh.

4
27.01.2020, 19:48

Теги

Похожие вопросы