Tại sao phép chia trong bash số học tính phần lớn là 0?


15

Cố gắng bash arithemetic cho một kịch bản, nhưng $ekhông cập nhật cho đến cuối. Đầu ra nói cho chính nó.

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

Tl; DR: Hiển thị 1..5 dưới dạng phần trăm.

Đầu ra:

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

Tại sao lại thế này?


1
Liên quan: Bash chỉ cung cấp số nguyên làm đầu ra bất kể đầu vào khi thực hiện các phép tính (Mặc dù, không giống như vấn đề được mô tả ở đây, điều đó không thể được giải quyết tốt bằng cách sắp xếp lại các hoạt động.)
Eliah Kagan

Câu trả lời:


24

bashkhông thể xử lý số học không nguyên. Nó sẽ cho bạn kết quả chính xác miễn là tất cả các biểu thức là số nguyên. Vì vậy, bạn cần tránh nhận được một giá trị không nguyên ở đâu đó trong tính toán của bạn.

Trong trường hợp của bạn, khi bạn đang đánh giá 1 / 5, 2 / 5v.v. , nó tạo ra các giá trị 0 nguyên trong các tương ứng bash với một số giá trị không nguyên và kết quả sẽ được đưa ra bằng 0. Ưu tiên của phép chia và phép nhân là các toán tử có cùng mức ưu tiên giống nhau luôn được thực hiện từ trái sang phải khi chúng được đặt trong biểu thức.

Một công việc xung quanh sẽ là thực hiện phép nhân trước và sau đó là phép chia để bash không bao giờ phải xử lý giá trị không nguyên. Biểu thức đã sửa sẽ là,

$ 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
Subexpression 1/5không tạo ra một giá trị không nguyên . Nó tạo ra giá trị số nguyên 0, vì kết quả của phép chia số nguyên 1 cho 5 là 0. Các hoạt động tiếp theo sử dụng thành công giá trị đó của 0. Đây không phải là những gì OP dự định, nhưng không có hoạt động nào tạo ra giá trị không nguyên và không có hoạt động nào thất bại.
Eliah Kagan

@EliahKagan cảm ơn bạn đã chỉ ra lỗ hổng. Đã chỉnh sửa.
souravc

16

Bash không làm tốt lắm với loại số học này ... Đây là vấn đề của bạn:

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

Nếu bạn cần xử lý các giá trị không nguyên, bạn có thể sử dụng 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

(cảm ơn @Arronical đã chỉ ra cách định dạng đầu ra dưới dạng số nguyên)


Tôi chắc chắn sẽ có một cái nhìn vào điều này, cảm ơn một lần nữa!
Cybex

1
Bạn có thể cắt .0từ đầu ra bằng cách thay đổi $percent tiếng vang của mình thành ${percent%.*}:)
Arronical

11

Không giống như bash, awk cung cấp số học dấu phẩy động đầy đủ. Ví dụ:

$ 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

Thử

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

:)

Xem phần ĐÁNH GIÁ ARITHMETIC của:

man bash

Nó chỉ hỗ trợ số nguyên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.