Чому поділ в арифметичній басі обчислює більшість відсотків як 0?


15

Спроба bash-арифметики для сценарію, але $eне оновлюється до кінця. Вихід говорить сам за себе.

max=5
for e in $(seq 1 1 $max); do 
    percent=$(( $e/$max*100 ))
    echo "echo $e / $max : = $percent"
done

Tl; DR: Відображення 1..5 у відсотках.

Вихід:

echo 1 / 5 : = 0
echo 2 / 5 : = 0
echo 3 / 5 : = 0
echo 4 / 5 : = 0
echo 5 / 5 : = 100

Чому це?


1
Пов'язане: Bash дає лише ціле число як вихідне, незалежно від вхідних даних при виконанні обчислень (Хоча, на відміну від описаної тут проблеми, це неможливо вирішити добре шляхом упорядкування операцій.)
Eliah Kagan

Відповіді:


24

bashне може обробити не цілу арифметику. Це дасть правильний результат, якщо всі вирази є цілими числами. Тому вам потрібно уникати отримання нецілого значення десь у вашому розрахунку.

У вашому випадку, коли ви оцінюєте 1 / 5, і 2 / 5т.д. , що створює ціле число нульових значень в БАШЕЄВ correspondings деяких нецілих значень і результати виходять рівний нуль відповідно. Пріоритет ділення та множення - це одне і те ж і те саме оператори прецеденту завжди виконуються зліва направо, оскільки вони розміщені у виразі.

Однією з задач буде спочатку зробити множення, а потім ділення, щоб bash ніколи не повинен обробляти не ціле значення. Виправлений вираз буде,

$ max=5; for e in $(seq 1 1 $max); do percent=$(( $e*100/$max )); echo "echo $e / $max : = $percent"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

1
Субекспресія 1/5не створює не ціле значення. Він створює ціле значення 0, оскільки результат цілого поділу 1 на 5 дорівнює 0. Подальші операції успішно використовують це значення 0. Це не те, що призначено в ОП, але жодна операція не створює неціле значення і жодна операція не завершується.
Елія Каган

@EliahKagan дякую, що вказали на недолік. Відредаговано.
souravc

16

Bash не дуже добре справляється з такою арифметикою ... Ось ваша проблема:

$ echo $((1/5))
0
$ echo $((2/5))
0
$ echo $((4/5))
0
$ echo $((4/5))
0

Якщо вам потрібно обробити не цілі значення, ви можете використовувати bc

$ max=5; for e in $(seq 1 1 "$max"); do percent=$(bc <<< "scale=1 ; $e/$max*100") ; echo "echo $e / $max : = ${percent%.*}"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

(спасибі @Arronical за вказівку, як форматувати вихід у вигляді цілих чисел)


Я обов'язково буду вивчати це, ще раз дякую!
Кібекс

1
Ви можете обрізати .0результат, змінивши $percent свій відгомін на ${percent%.*}:)
Arronical

11

На відміну від bash, awk пропонує повну арифметику з плаваючою точкою. Наприклад:

$ awk -v max=5 'BEGIN{for (e=1;e<=max;e++) print "echo " e " / " max " : = " 100*e/max}' 
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

5

Спробуйте

percent=$(( $e*100/$max ))

:)

Дивіться розділ АРИТМЕТИЧНА ОЦІНКА:

man bash

Він підтримує лише цілі числа.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.