![测试极限:](https://linux22.com/image/99245/%E6%B5%8B%E8%AF%95%E6%9E%81%E9%99%90%EF%BC%9A.png)
这是一个探索性问题,也就是说我不完全确定这个问题是关于什么的,但我认为它与 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
答案1
重击使用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
以二进制形式。
正如指出的伊尔卡丘的回答,移位在 64 位上采用偏移量模 64x86CPU(无论使用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))
答案2
来自2.05bCHANGES
的文件bash
:
j。 shell 现在以机器支持的最大整数大小 (intmax_t) 执行算术,而不是 long。
在 x86_64 机器上intmax_t
对应于有符号 64 位整数。所以你会得到-2^63
和之间有意义的值2^63-1
。超出该范围,您只会得到环绕。
答案3
移位 1024 得到 1,因为移位量实际上是对位数 (64) 取模,因此1024 === 64 === 0
、 和1025 === 65 === 1
。
移位除 a 之外的其他值1
可以清楚地表明这不是位旋转,因为在移位值(至少)为 64 之前,高位不会回绕到低端:
$ printf "%x\n" $(( 5 << 63 )) $(( 5 << 64 ))
8000000000000000
5
此行为可能取决于系统。这bash 代码 Stephen 链接到仅显示简单的移位,而不检查右手值。如果我没记错的话,x86 处理器仅使用移位值的底部六位(在 64 位模式下),因此该行为可能直接来自机器语言。另外,我认为 C 中也没有明确定义移位超过位宽(gcc
对此发出警告)。
答案4
通过移位产生一个整数。我能走多远?
直到整数表示环绕(大多数 shell 中的默认设置)。
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]的。
[一]来自:英特尔® 64 和 IA-32 架构软件开发人员手册:第 2 卷计数被屏蔽为 5 位(如果在 64 位模式下并且使用 REX.W,则为 6 位)。计数范围限制为 0 到 31(如果使用 64 位模式且使用 REX.W,则为 63)。。
另外:记住那$((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"
嘘
翻译成几乎任何 shell:
#!/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"
对许多 shell 运行上述命令,所有(bash 2.04 和 mksh 除外)在此计算机上
接受的值最大为 ( )。2**63 -1
有趣的是报告说att shell:
$ attsh --version
version sh (AT&T Research) 93u+ 2012-08-01
但在 的值上打印错误$((2^63))
,而不是 ksh 。