Округление чисел с плавающей точкой
Что означает «округление числа с плавающей запятой»?
Это легко, очевидно ... Где моя математическая книга из школы ...
Нет, мы уже знаем, что ничего, связанного с числами с плавающей запятой, не так просто:
Для начала есть несколько режимов округления:
Округление вверх?
Округление вниз?
Округление до нуля?
Округление до ближайшего - связь до четного?
Округление до ближайшего - связи от нуля?
Как обращаться с угловыми корпусами? Как узнать, какие угловые случаи?
Хорошо, похоже, нам лучше использовать реализацию стандарта 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.