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 printf
puede 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 x
a p
precisió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 x
entrada estándar y p
como 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/printf
lugar del shell incorporado bash
o zsh
para evitar inconsistencias menores en la implementación de las printf
variantes, y evita un efecto muy sucio cuando, en un entorno local alemán, LC_ALL
se establece, pero no se exporta. Entonces, el builtin usa ,
, y /usr/bin/printf
usa .
...
Consulte también %g
para redondear a un número específico de dígitos significativos.
awk
.