¿Cómo redondear números de coma flotante en shell?

13

¿Cómo redondeo correctamente los números de coma flotante IEEE 754 en la línea de comando?

Quiero especificar la precisión del número de salida: el recuento de dígitos fraccionarios.

Redondear 6.66a precisión 1debería dar 6.7, por ejemplo. Más en la tabla a continuación:

Value   Precision  Rounded
6.66    0          7
6.66    1          6.7
6.66    2          6.66
6.66    3          6.660
6.666   3          6.666
6.6666  3          6.667

Debería ser utilizable en un shell interactivo, pero idealmente lo suficientemente robusto para usarlo en scripts de shell de producción.

Volker Siegel
fuente
Me gustaría utilizar awk.
isomorfismos el

Respuestas:

30

Redondeo de números de coma flotante

¿Qué significa "redondear un número de coma flotante"?
Eso es fácil, obviamente ... ¿Dónde está mi libro de matemáticas de la escuela ...

No, ya sabemos que nada relacionado con los números de coma flotante es fácil:

Para empezar, hay múltiples modos de redondeo:

Redondeando hacia arriba?
Redondeando hacia abajo?
Redondeando a cero?
Redondeando al más cercano - ¿Lazos al par?
Redondeando al más cercano - ¿Lazos lejos de cero?

¿Cómo manejar las vitrinas? ¿Cómo saber cuáles son los casos de esquina?

OK, parece que mejor usamos una implementación del estándar IEEE 754 y dejamos que nuestro sistema se encargue de eso.

Para redondear un número de coma flotante en el shell, basado en la aritmética de coma flotante estándar, necesitamos tres pasos:

  • Convierta el texto de entrada de un argumento de línea de comando a un número estándar de coma flotante.
  • Redondea el número de coma flotante utilizando la implementación normal de IEEE 754.
  • Formatee el número como una cadena para la salida.

Resulta que el comando de shell printfpuede hacer todo esto. Se puede usar para imprimir números de acuerdo con una especificación de formato como se describe enman 3 printf . Los números se redondean implícitamente en la forma estándar si es necesario para el formato de salida:

El comando

Redondear xa pprecisión de dígitos con entrada como argumentos de línea de comando:

printf "%.*f\n" "$p" "$x"

O en una tubería de shell, con entrada de xentrada estándar y pcomo argumento:

echo "$x" | xargs printf "%.*f\n" "$p"

Ejemplos:

$ printf '%.*f\n' 0 6.66
7
$ printf '%.*f\n' 1 6.66
6.7
$ printf '%.*f\n' 2 6.66
6.66
$ printf '%.*f\n' 3 6.66
6.660
$ printf '%.*f\n' 3 6.666
6.666
$ printf '%.*f\n' 3 6.6666
6.667

Trampas malas

¡Cuidado con la configuración regional! Especifica el separador entre la parte integral y la fracción: la ., como es de esperar.
Pero mira lo que sucede en una localidad alemana, por ejemplo:

$ LC_ALL=de_DE.UTF-8 printf '%.*f\n' 3 6.6666
6,667

Sí, así es 6,667, seis coma seis seis siete. Eso arruinaría tu script con seguridad.
(Pero solo para los dos clientes en Alemania. A excepción de las máquinas del desarrollador que actualmente depuran para estos clientes).

Más robusto

Para hacerlo más robusto, use:

LC_ALL=C /usr/bin/printf "%.*f\n" "$p" "$x"

o

echo "$x" | LC_ALL=C xargs /usr/bin/printf "%.*f\n" "$p"

Esto también utiliza en /usr/bin/printflugar del shell incorporado basho zshpara evitar inconsistencias menores en la implementación de las printfvariantes, y evita un efecto muy sucio cuando, en un entorno local alemán, LC_ALLse establece, pero no se exporta. Entonces, el builtin usa ,, y /usr/bin/printfusa ....


Consulte también %gpara redondear a un número específico de dígitos significativos.

Volker Siegel
fuente
0

Lo siguiente funcionó para mí.

#!/bin/bash
function float() {
bc << EOF
num = $1;
base = num / 1;
if (((num - base) * 10) > 1 )
    base += 1;
print base;
EOF
echo ""
}

float 3.2
xs2rashid
fuente