Esta noticia ha sido analizado por especialistas para que tengas la seguridad de la veracidad de nuestro contenido.
Solución:
Use AWK (sin bash-ismos):
item=30
total=70
percent=$(awk "BEGIN pc=100*$item/$total; i=int(pc); print (pc-i<0.5)?i:i+1 ")
echo $percent
43
Tomando 2 * el cálculo de porcentaje original y obteniendo el módulo 2 de eso proporciona el incremento para el redondeo.
item=30
total=70
percent=$((200*$item/$total % 2 + 100*$item/$total))
echo $percent
43
(probado con bash, ash, dash y ksh)
Esta es una implementación más rápida que disparar un coproceso AWK:
$ pa() for i in `seq 0 1000`; do pc=$(awk "BEGIN pc=100*$item/$total; i=int(pc); print (pc-i<0.5)?i:i+1 "); done;
$ time pa
real 0m24.686s
user 0m0.376s
sys 0m22.828s
$ pb() for i in `seq 0 1000`; do pc=$((200*$item/$total % 2 + 100*$item/$total)); done;
$ time pb
real 0m0.035s
user 0m0.000s
sys 0m0.012s
Solo se requiere un script de shell compatible con POSIX para admitir entero aritmética usando el lenguaje de concha ("solo se requiere aritmética de enteros largos con signo"), por lo que un cáscara pura la solución debe emular aritmética de coma flotante[1]:
item=30
total=70
percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
100 * item / total
produce el truncado resultado de la entero división como un porcentaje.1000 * item / total % 10 >= 5 ? 1 : 0
calcula el 1er decimal, y si es igual o mayor a 5, suma 1 al resultado entero para redondearlo.- Tenga en cuenta cómo hay no Necesitar prefix referencias variables con
$
dentro de una expansión aritmética$((...))
.
Si, en contradicción con la premisa de la pregunta, el uso de utilidades externas es aceptable:
awk
ofrece una sencillo solución, que, sin embargo, viene con la consideración que usa true Precisión doble binario valores de punto flotante y, por lo tanto, puede producir resultados inesperados en decimal representación - por ejemplo, intenteprintf '%.0fn' 28.5
cuyos rendimientos28
en lugar de lo esperado29
):
awk -v item=30 -v total=70 'BEGIN printf "%.0fn", 100 * item / total '
- Tenga en cuenta cómo
-v
se utiliza para definir variables para elawk
script, que permite una clara separación entre el entre comillas simples y por lo tanto literalawk
script y cualquier valor que se le haya pasado desde el cáscara.
- Por el contrario, aunque
bc
es una utilidad POSIX (y, por lo tanto, se puede esperar que esté presente en la mayoría de las plataformas similares a Unix) y realiza precisión arbitraria aritmética, invariablemente trunca los resultados, por lo que redondeo debe ser realizado por otro utilidad;printf
sin embargo, aunque en principio es una utilidad POSIX, es no requerido para admitir especificadores de formato de coma flotante (como los que se usan dentroawk
arriba), por lo que el siguiente puede o no funcionar (y no vale la pena, dado el simpleawk
solución, y dado que los problemas de precisión debido a la aritmética de punto flotante están de vuelta en la imagen):
# !! This MAY work on your platform, but is NOT POSIX-compliant:
# `-l` tells `bc` to set the precision to 20 decimal places, `printf '%.0fn'`
# then performs the rounding to an integer.
item=20 total=70
printf '%.0fn' "$(bc -l <
[1] Sin embargo, POSIX permite soporte no entero "El shell puede usar un tipo flotante real en lugar de firmado siempre que no afecte los resultados en los casos en que no haya desbordamiento". En la práctica, ksh
y zsh
. admite aritmética de coma flotante si lo solicitaspero no bash
y dash
. Si desea ser compatible con POSIX (ejecutar a través de /bin/sh
), siga con la aritmética de enteros. En todos los shells, la división de enteros funciona como de costumbre: el cociente se devuelve, que es el resultado de la división con cualquier parte fraccionaria truncada (eliminada).
valoraciones y comentarios
Si haces scroll puedes encontrar las observaciones de otros usuarios, tú de igual manera eres capaz mostrar el tuyo si te gusta.