Побітове зсув і найбільше ціле число в Bash


16

Це питання розвідки, тобто я не зовсім впевнений, про що йдеться в цьому питанні, але я думаю, що це найбільше ціле число в Bash. Так чи інакше, я визначу це напружено.

$ echo $((1<<8))
256

Я створюю ціле число, трохи зміщуючи. Як далеко я можу зайти?

$ echo $((1<<80000))
1

Мабуть, не так далеко. (1 несподівано, і я повернусь до нього.) Але,

$ echo $((1<<1022))
4611686018427387904

все ще позитивний. Не це, однак:

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

І ще крок далі,

$ echo $((1<<1024))
1

Чому 1? І чому наступне?

$ echo $((1<<1025))
2
$ echo $((1<<1026))
4

Хтось хотів би проаналізувати цю серію?

ОНОВЛЕННЯ

Моя машина:

$ uname -a
Linux tomas-Latitude-E4200 4.4.0-47-generic #68-Ubuntu SMP Wed Oct 26 19:39:52 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

-9223372036854775808 = 0xF333333333333334. Це смішний вигляд краю. Звичайно, 4611686018427387904 = 0x4000000000000000. Я підозрюю, що ви потрапили в якусь обертівку щодо кількості бітів, на яку потрібно змінити. Навіщо ти це робиш?
CVn

6
@ MichaelKjörling Для розваг

2
@ MichaelKjörling Ні, це не так. -9223372036854775808 було б 0x8000000000000000. Під час перевірки ви залишили остаточну цифру: -922337203685477580 буде 0xF333333333333334.
hvd

Відповіді:


27

Bash використовує intmax_tзмінні для арифметики . У вашій системі це 64 біти в довжину, тому:

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

який є

100000000000000000000000000000000000000000000000000000000000000

у двійковій (1, а потім 62 0s). Знову перемістіть:

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

який є

1000000000000000000000000000000000000000000000000000000000000000

у двійковій (63 0s), у двох арифметичних доповненнях двох.

Щоб отримати найбільше представлене ціле число, потрібно відняти 1:

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

який є

111111111111111111111111111111111111111111111111111111111111111

у бінарному.

Як було відзначено в ilkkachu «s відповідь , зрушуючи бере зміщення по модулю 64 на 64-розрядних x86 процесорів (незалежно від використання RCLабо SHL), який пояснює поведінку , яке ви бачите:

$ 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))

1
Ні, вони вам потрібні, Бінарний -має перевагу вище, ніж <<.
cuonglm

1
@cuonglm так, служить мені для тестування на zsh ... Дякую ще раз!
Стівен Кітт

@cuonglm та Стівен. Ну, це гарна редакція. echo $((1<<63-1))дає мені 4611686018427387904.

@tomas yup, bash використовує пріоритет C оператора, zsh за замовчуванням має власну, де $((1<<63-1))дорівнює $(((1<<63)-1)).
Стівен Кітт

Це добре знати, гарне запитання та дуже вдячна відповідь, завдяки вам і Стівен Кітт, і Томас.
Валентин Б.

4

З CHANGESфайлу за bash2.05b:

j. Тепер оболонка виконує арифметику у найбільшому цілому розмірі, який підтримує машина (intmax_t), а не довгий.

На x86_64 машини intmax_tвідповідають підписаним 64-бітовим цілим числом. Так ви отримуєте змістовні значення між -2^63і 2^63-1. Поза межами цього діапазону ви просто обертаєте.


Нітпік: між -2^63і 2^63-1, включно.
Номінальна тварина

4

Зсув на 1024 дає одиницю, оскільки сума зсуву ефективно приймається за модулем на кількість бітів (64), так 1024 === 64 === 0, і 1025 === 65 === 1.

Зсув чогось іншого, ніж 1виразний, дає зрозуміти, що це не трохи обертання, оскільки більш високі біти не обертаються до нижнього кінця до того, як значення зсуву буде (принаймні) 64:

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

Можливо, така поведінка залежить від системи. Код Баш Стівен пов'язаний показує тільки простий зсув, без яких - або перевірок на правильне значення руки. Якщо я правильно пам'ятаю, процесори x86 використовують лише шість нижчих бітів значення зсуву (в 64-бітному режимі), тому поведінка може бути безпосередньо з машинної мови. Крім того, я думаю, що зрушення на більшу ширину бітів також чітко не визначені в C ( gccпопереджає про це).


2

виробляючи ціле число, переміщуючи трохи. Як далеко я можу зайти?

Поки ціле представлення не завершиться (за замовчуванням у більшості оболонок).
64-бітове ціле число зазвичай обертається на 2**63 - 1.
Це 0x7fffffffffffffffабо 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 кроків для більшості комп'ютерів у будь-якому разі, це не буде занадто повільно.

баш

Спочатку нам потрібно найбільше ціле число форми 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):

#!/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"

ш

Перекладається майже на будь-яку оболонку:

#!/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, хоча.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.