Ограничение точности числовых переменных с плавающей запятой в Bash

Вопрос или проблема

В Ubuntu 14.04.1 LTS 64-bit bash я объявляю переменные с плавающей точкой, умножая переменные с плавающей точкой в bash через bc, с установкой масштаба на 3; однако, я не могу сделать так, чтобы количество цифр после десятичной точки стало нулевым, и избавиться от нуля слева от десятичной точки. Как я могу преобразовать, скажем, 0.005000000 в .005? Это необходимо из-за моей системы наименования файлов. Благодарю за рекомендации.

ОБНОВЛЕНИЕ: Могу ли я использовать это для уже определённых переменных оболочки и переопределять их? Следующий код выдаёт ошибку.

~/Desktop/MEEP$ printf "%.3f\n" $w
bash: printf: 0.005000: invalid number
0,000

Вывод locale

@vesnog:~$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=tr_TR.UTF-8
LC_TIME=tr_TR.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=tr_TR.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=tr_TR.UTF-8
LC_NAME=tr_TR.UTF-8
LC_ADDRESS=tr_TR.UTF-8
LC_TELEPHONE=tr_TR.UTF-8
LC_MEASUREMENT=tr_TR.UTF-8
LC_IDENTIFICATION=tr_TR.UTF-8
LC_ALL=

Вывод echo $w

@vesnog:~$ echo $w
0.005000

Простой способ — использовать printf:

$ printf "%.3f\n" 0.005000000000
0.005

Чтобы удалить ведущий 0, просто уберите его с помощью sed:

$ printf "%.3f\n" 0.005000000000 | sed 's/^0//'
.005

Существует специальная переменная под названием scale. Вы можете настроить эту переменную для ограничения точности.

ПРИМЕР

$ echo "300/7" | bc -l
42.85714285714285714285

Чтобы ограничить точность,

$ echo "scale=2; 300/7" | bc -l
42.85

ОБНОВЛЕНО

$ echo "scale=3; 300/7" | bc -l | sed 's/[0-9]*\././g'
.857

Эта команда sed удаляет конечные нули:

sed 's/\(0\.0*[1-9][1-9]*\)0*/\1/'

Она запоминает любые значения, начинающиеся с 0., за которыми следуют нули, за которыми идёт 1 или более цифр от 1 до 9, а затем ноль или более нулей. Затем она обрезает ноль или более конечных нулей.

Это позволяет удалить произвольное количество конечных нулей и сохранить любое произвольно малое число, большее нуля, независимо от того, насколько оно мало.

Например

0.054000 становится 0.054, или 0.00000010000000 становится 0.0000001 и так далее.

Для получения дополнительной информации ознакомьтесь с замечательным руководством по sed на Grymoire.

Понимаю, что поздновато, но для всех новых людей, которые гуглят эту проблему.
Проблема с “недопустимым числом” не была решена в самом популярном ответе, фактически, ответ также выдаёт ошибку недопустимого числа.

Причина — переменная LC_NUMERIC из locale. Простое решение: используйте “,” вместо “.” в качестве десятичной точки. Еще проще — измените локальную переменную.

Тест

export LC_NUMERIC="en_US.UTF-8"

Постоянно

echo 'export LC_NUMERIC="en_US.UTF-8"' >>~/.bashrc

Почему не grep?

echo "$your_number" | grep -o '.*[1-9]'

Есть также возможность использовать встроенный в Bash Параметр Замены вместе с Расширенным Шаблоном Соответствия в два шага.

Для данного массива чисел с плавающей точкой, мы можем сначала удалить любые ведущие нули, а затем ограничить количество цифр после десятичной точки:

#! /bin/bash

# Включаем расширенный шаблон соответствия
shopt -s extglob

floats=('0.000919104'
'0.1000919104'
'2.1000919104'
'12001.1000919104'
'0012001.1000919104'
'2001.1000919104'
'2001.011000919104'
'0.9011000919104'
'00.19011000919104'
'00.0019011000919104'
'006100.0019011000919104'
'.0019011000919104'
'.019011000919104'
'.19011000919104')

result=()

# Удаляем ведущие нули, обрабатываем каждое очищенное число с плавающей точкой и ограничиваем
# количество цифр после запятой, собирая результаты
for f in "${floats[@]##*(0)}"; do 
    result+=("${f%"${f##*.*(0)[^0]?}"}")
done

# Сравнение ввода и собранного вывода 
printf '%s\n%s\n' 'INPUT:' "${floats[@]}"
printf '%s\n%s\n' 'OUTPUT:' "${result[@]}"

# Результат будет таким:

INPUT:
0.000919104
0.1000919104
2.1000919104
...

OUTPUT:
.00091
.10
2.10
12001.10
12001.10
2001.10
2001.011
.90
.19
.0019
6100.0019
.0019
.019
.19

Мы также можем удалять ведущие нули внутри цикла (по floats), например, с помощью f="${f##*(0)}", чтобы избежать неявного двойного цикла.

Ограничение числа цифр после десятичной точки выполняется внутри цикла, для каждого элемента, через "${f%"${f##*.*(0)[^0]?}"}".

Это то, что я бы назвал “псевдо-вложенной Параметр Замены“, когда из обрабатываемого числа с плавающей точкой удаляется подстрока справа налево, начинающаяся с произвольного количества символов, за которой следует разделитель плавающей точки ‘.’, за которым следуют (опционально) нули, одна не-ноль цифра и одна произвольная цифра.

Так как работа идет со строками на этом этапе, округление не применяется.

Ограничение цифр (здесь 2) после десятичной точки фактически определяется количеством использованных ? соответствий, или 1+n, если можно так выразиться.

Но обратите внимание, что этот метод будет иметь проблемы с числами с плавающей точкой, содержащими разделители тысяч (например, что-то вроде ‘2.401.360,3430’). Поэтому используйте только ‘,’ или что-нибудь, кроме ‘.’, в качестве разделителя плавающей точки.

.

Ответ или решение

Вопрос касается работы с плавающими числами в Bash на Ubuntu 14.04.1 LTS 64-bit, где основная проблема заключается в форматировании чисел для соответствия определенной схеме именования файлов. Пользователь пытается отформатировать плавающее число, например, 0.005000000, чтобы оно выглядело как .005, удалив ведущий нуль и лишние нули после значащих цифр. При этом возникает ошибка в команде printf, связанная с конфигурацией locale.

Теория

Bash не поддерживает плавающую точку нативно, что требует использования вспомогательных инструментов, таких как bc (аритметика в терминале) для выполнения операций с плавающей точкой. bc позволяет задать точность вычислений с помощью установки значения переменной scale. С этой командой можно управлять количеством знаков после запятой, но это не решает проблемы с форматированием вывода в целях согласованности стиля именования файлов.

Появление ошибки, связанной с printf, вероятно, обусловлено региональными настройками операционной системы, в частности переменной LC_NUMERIC, которая управляет форматом чисел. Если этот параметр установлен на использование запятой как десятичного разделителя, printf может не корректно интерпретировать число с точкой. Таким образом, вам нужно либо изменить locale, либо учесть эту особенность при работе с данными.

Пример

Для иллюстрации можно использовать следующую комбинацию команд, чтобы прийти к желаемому формату числа:

  1. Работа с bc для форматирования числа:

    number=$(echo "scale=3; your_expression" | bc -l)

    Здесь scale=3 задает точность до трех знаков после запятой.

  2. Исправление региональных настроек:

    Проверьте ваши текущие настройки locale:

    locale

    Если LC_NUMERIC отличается от en_US.UTF-8, вы можете временно изменить это для текущей сессии:

    export LC_NUMERIC="en_US.UTF-8"

    Для постоянного изменения добавьте:

    echo 'export LC_NUMERIC="en_US.UTF-8"' >> ~/.bashrc
  3. Форматирование вывода с помощью sed:

    После получения числа воспользуйтесь sed, чтобы удалить ненужные символы:

    formatted_number=$(echo $number | sed 's/^0//; s/\(.\d*[1-9]\)0*$/\1/')

    Это удаляет ведущий ноль и обрезает лишние нули в конце.

Применение

Рассмотрим реальное применение этой техники для именования файлов. Если имя файла должно начинаться со значимого числа без ведущего нуля, использование этой методологии позволит соблюсти формат, избегая ошибок и приводя к нужному представлению. Например, если ваш исходный результат вычисления равен 0.005000, переводы и преобразования помогут достичь варианта .005.

Теперь, переходя к программному применению:

#!/bin/bash

# Процесс вычислений
w=$(echo "0.5/100" | bc -l)

# Исправление вывода и форматирование
result=$(echo $w | sed 's/^0//; s/\(.\d*[1-9]\)0*$/\1/')

echo "Итоговое отформатированное число: $result"

Этот скрипт отделяет результат вычислений, обрабатывает его через sed и выводит число в задаваемой вами схеме именования

Применение данной методики гарантирует, что ваши файлы будут организованы и легко идентифицируемы по структурированным именам, что критично для эффективной работы и поддержания порядка в файловой системе вашего проекта.

Оцените материал
Добавить комментарий

Капча загружается...