produciendo un número entero cambiando un poco. ¿Hasta dónde puedo llegar?
Hasta que la representación entera se envuelva (el valor predeterminado en la mayoría de los shells).
Un entero de 64 bits generalmente se envuelve en 2**63 - 1
.
Eso es 0x7fffffffffffffff
o 9223372036854775807
en diciembre.
Ese número '+1' se vuelve negativo.
Eso es lo mismo que 1<<63
, por lo tanto:
$ echo "$((1<<62)) $((1<<63)) and $((1<<64))"
4611686018427387904 -9223372036854775808 and 1
Después de eso, el proceso se repite nuevamente.
$((1<<80000)) $((1<<1022)) $((1<<1023)) $((1<<1024)) $((1<<1025)) $((1<<1026))
El resultado depende mod 64
del valor de desplazamiento [a] .
[a] De: Intel® 64 e IA-32 Architectures Software Developer's Manual: Volumen 2 El recuento se enmascara a 5 bits (o 6 bits si está en modo de 64 bits y se utiliza REX.W). El rango de conteo está limitado de 0 a 31 (o 63 si se usa el modo de 64 bits y REX.W). .
Además: recuerda que $((1<<0))
es1
$ 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
Entonces, todo depende de qué tan cerca esté el número a un múltiplo de 64.
Probar el límite:
La forma sólida de probar cuál es el número entero positivo (y negativo) máximo es probar cada bit por turno. De todos modos, tiene menos de 64 pasos para la mayoría de las computadoras, no será demasiado lento.
golpetazo
Primero necesitamos el mayor número entero de la forma 2^n
(conjunto de 1 bit seguido de ceros). Podemos hacerlo desplazándonos hacia la izquierda hasta que el próximo desplazamiento haga que el número sea negativo, también llamado "ajuste"
a=1; while ((a>0)); do ((b=a,a<<=1)) ; done
Dónde b
está el resultado: el valor antes del último turno que falla el ciclo.
Luego, debemos intentar cada poco para descubrir cuáles afectan el signo de e
:
c=$b;d=$b;
while ((c>>=1)); do
((e=d+c))
(( e>0 )) && ((d=e))
done;
intmax=$d
El número entero máximo ( intmax
) resulta del último valor de d
.
En el lado negativo (menos de 0
), repetimos todas las pruebas, pero comprobamos cuándo se puede hacer un bit 0 sin ajustar.
Una prueba completa con la impresión de todos los pasos es la siguiente (para 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
Traducido a casi cualquier 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"
Al ejecutar lo anterior para muchos shells,
todos (excepto bash 2.04 y mksh) aceptaron valores hasta ( 2**63 -1
) en esta computadora.
Es interesante informar que el shell att :
$ attsh --version
version sh (AT&T Research) 93u+ 2012-08-01
imprimió un error en los valores de $((2^63))
, no ksh sin embargo.