Чому поділ в арифметичній басі обчислює більшість відсотків як 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.