Округление чисел с плавающей точкой
Что означает «округление числа с плавающей запятой»?
Это легко, очевидно ... Где моя математическая книга из школы ...
Нет, мы уже знаем, что ничего, связанного с числами с плавающей запятой, не так просто:
Для начала есть несколько режимов округления:
Округление вверх?
Округление вниз?
Округление до нуля?
Округление до ближайшего - связь до четного?
Округление до ближайшего - связи от нуля?
Как обращаться с угловыми корпусами? Как узнать, какие угловые случаи?
Хорошо, похоже, нам лучше использовать реализацию стандарта IEEE 754, и пусть наша система позаботится об этом.
Чтобы округлить число с плавающей запятой в оболочке на основе стандартной арифметики с плавающей запятой, нам нужно выполнить три шага:
- Преобразуйте входной текст из аргумента командной строки в стандартное число с плавающей запятой.
- Округлите число с плавающей запятой, используя обычную реализацию IEEE 754.
- Отформатируйте число как строку для вывода.
Оказывается, что команда оболочки printf
может сделать все это. Он может использоваться для печати чисел в соответствии со спецификацией формата, как описано в man 3 printf
. Числа округляются неявным образом стандартным способом, если это требуется для выходного формата:
Команда
Округление x
до p
точности цифр с вводом в качестве аргументов командной строки:
printf "%.*f\n" "$p" "$x"
Или в конвейере оболочки, с вводом x
на стандартный ввод и в p
качестве аргумента:
echo "$x" | xargs printf "%.*f\n" "$p"
Примеры:
$ 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
Плохие ловушки
Остерегайтесь локали! Он определяет разделитель между целой и дробной частью - .
как вы можете ожидать.
Но посмотрите сами, что происходит в немецком языке, например:
$ LC_ALL=de_DE.UTF-8 printf '%.*f\n' 3 6.6666
6,667
Да, все верно 6,667
- шесть запятых шесть шесть семь. Это наверняка испортит ваш сценарий.
(Но только для двух клиентов в Германии. За исключением машин разработчика, отлаживающих в настоящее время для этих клиентов.)
Более надежный
Чтобы сделать его более надежным, используйте:
LC_ALL=C /usr/bin/printf "%.*f\n" "$p" "$x"
или
echo "$x" | LC_ALL=C xargs /usr/bin/printf "%.*f\n" "$p"
Это также использует /usr/bin/printf
вместо встроенной оболочки bash
или zsh
для обхода незначительных несоответствий в реализации printf
вариантов и предотвращает очень грязный эффект, когда в немецком языке LC_ALL
установлен, но не экспортирован. Затем встроенный использует ,
и /usr/bin/printf
использует .
...
См. Также %g
для округления до указанного числа значащих цифр.
awk
.