测试极限:

测试极限:

这是一个探索性问题,也就是说我不完全确定这个问题是关于什么的,但我认为它与 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(无论使用RCLSHL),这解释了您所看到的行为:

$ 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
那是0x7fffffffffffffff9223372036854775807在十二月。

该数字“+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 。

相关内容