Tôi muốn so sánh hai số dấu phẩy động trong một tập lệnh shell. Đoạn mã sau không hoạt động:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Tôi muốn so sánh hai số dấu phẩy động trong một tập lệnh shell. Đoạn mã sau không hoạt động:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Câu trả lời:
Bạn có thể kiểm tra riêng phần nguyên và phần phân số:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
Như fered nói trong các bình luận, nó chỉ hoạt động nếu cả hai số có các phần phân số và cả hai phần phân số có cùng số chữ số. Đây là phiên bản hoạt động cho số nguyên hoặc phân số và bất kỳ toán tử bash nào:
#!/bin/bash
shopt -s extglob
fcomp() {
local oldIFS="$IFS" op=$2 x y digitx digity
IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
digitx=${x[1]:0:1} digity=${y[1]:0:1}
(( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
x[1]=${x[1]:1} y[1]=${y[1]:1}
done
[[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
[[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
(( ${x:-0} $op ${y:-0} ))
}
for op in '==' '!=' '>' '<' '<=' '>='; do
fcomp $1 $op $2 && echo "$1 $op $2"
done
1.00000000000000000000000001lớn hơn 2.
Bash không hiểu số học dấu phẩy động. Nó xử lý các số có chứa một dấu thập phân dưới dạng chuỗi.
Sử dụng awk hoặc bc thay thế.
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
Nếu bạn có ý định thực hiện nhiều phép toán, có lẽ tốt hơn là dựa vào python hoặc perl.
Bạn có thể sử dụng gói num-utils cho các thao tác đơn giản ...
Đối với các môn toán nghiêm trọng hơn, hãy xem liên kết này ... Nó mô tả một số tùy chọn, ví dụ.
Một ví dụ của numprocess
echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087
A programs for dealing with numbers from the command line
The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.
Includes these programs:
* numaverage: A program for calculating the average of numbers.
* numbound: Finds the boundary numbers (min and max) of input.
* numinterval: Shows the numeric intervals between each number in a sequence.
* numnormalize: Normalizes a set of numbers between 0 and 1 by default.
* numgrep: Like normal grep, but for sets of numbers.
* numprocess: Do mathematical operations on numbers.
* numsum: Add up all the numbers.
* numrandom: Generate a random number from a given expression.
* numrange: Generate a set of numbers in a range expression.
* numround: Round each number according to its value.
Đây là một bashhack ... Nó thêm số 0 đứng đầu vào số nguyên để làm cho một chuỗi so sánh từ trái sang phải có ý nghĩa. Đoạn mã đặc biệt này yêu cầu cả min và val thực sự có dấu thập phân và ít nhất một chữ số thập phân.
min=12.45
val=10.35
MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min
đầu ra:
min=10.35
Đối với các phép tính đơn giản trên các số dấu phẩy động (+ - * / và so sánh), bạn có thể sử dụng awk.
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
Hoặc, nếu bạn có ksh93 hoặc zsh (không phải bash), bạn có thể sử dụng số học tích hợp trong vỏ của mình, hỗ trợ các số dấu phẩy động.
if ((min>val)); then ((val=min)); fi
Để tính toán dấu phẩy động nâng cao hơn, hãy tra cứu bc . Nó thực sự hoạt động trên các số điểm cố định chính xác tùy ý.
Lệnh sortcó một tùy chọn -g( --general-numeric-sort) có thể được sử dụng để so sánh trên <, "nhỏ hơn" hoặc >"lớn hơn", bằng cách tìm mức tối thiểu hoặc tối đa.
Những ví dụ này đang tìm kiếm mức tối thiểu:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
Nó hoạt động với ký hiệu khá chung về số dấu phẩy động, giống như với Ký hiệu điện tử
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
Lưu ý E-10, làm cho số đầu tiên 0.000000001245, thực sự ít hơn 10.35.
Tiêu chuẩn dấu phẩy động, IEEE754 , xác định một số giá trị đặc biệt. Đối với những so sánh này, những điều thú vị là INFvô cùng. Ngoài ra còn có vô cực tiêu cực; Cả hai đều được xác định rõ giá trị trong tiêu chuẩn.
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
Để tìm mức sử dụng tối đa sort -grthay vì sort -g, đảo ngược thứ tự sắp xếp:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
Để thực hiện <so sánh ("nhỏ hơn"), do đó, nó có thể được sử dụng trong ifvv, so sánh mức tối thiểu với một trong các giá trị. Nếu mức tối thiểu bằng giá trị, được so sánh dưới dạng văn bản , nó nhỏ hơn giá trị khác:
$ a=12.45; b=10.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
0
a == min(a, b)là giống như a <= b. Điều đáng chú ý là điều này không kiểm tra nghiêm ngặt ít hơn mặc dù. Nếu bạn muốn làm điều đó, bạn cần kiểm tra a == min(a, b) && a != max(a, b), bằng cách kháca <= b and not a >= b
Chỉ cần sử dụng ksh( ksh93chính xác) hoặc zsh, cả hai đều hỗ trợ mỹ phẩm điểm nổi:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
Chỉnh sửa: Xin lỗi, tôi đã bỏ lỡ ksh93 đã được đề xuất. Giữ câu trả lời của tôi chỉ để làm rõ tập lệnh được đăng trong câu hỏi mở có thể được sử dụng mà không có thay đổi bên ngoài chuyển đổi shell.
Chỉnh sửa2: Lưu ý rằng ksh93yêu cầu nội dung biến phải phù hợp với ngôn ngữ của bạn, tức là với ngôn ngữ Pháp, phải sử dụng dấu phẩy thay vì dấu chấm:
...
min=12,45
val=10,35
...
Một giải pháp mạnh mẽ hơn là đặt ngôn ngữ ở đầu tập lệnh để đảm bảo nó sẽ hoạt động bất kể ngôn ngữ của người dùng:
...
export LC_ALL=C
min=12.45
val=10.35
...
.(vì vậy không ở một nửa thế giới có dấu phân cách thập phân ,). zshkhông có vấn đề đó
LC_ALL, điều đó cũng có nghĩa là các số sẽ không được hiển thị (hoặc đầu vào) ở định dạng ưa thích của người dùng. Xem unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/ợi để biết cách tiếp cận tốt hơn.
.dù sao tôi cũng cho rằng dấu phân cách ưa thích của anh ta .
min=$(echo "${min}sa ${val}d la <a p" | dc)
Điều đó sử dụng dcmáy tính để sxé giá trị $mintrong thanh ghi avà dtăng giá trị $vallên trên đỉnh của ngăn xếp thực thi chính của nó. Sau đó, nó lđưa nội dung alên trên cùng của ngăn xếp, tại điểm đó trông giống như:
${min} ${val} ${val}
Cửa <sổ bật lên hai mục trên cùng của ngăn xếp và so sánh chúng. Vì vậy, ngăn xếp sau đó trông giống như:
${val}
Nếu mục trên cùng nhỏ hơn mục thứ hai lên trên, nó sẽ đẩy nội dung alên trên cùng, vì vậy ngăn xếp trông như sau:
${min} ${val}
Khác nó không làm gì và ngăn xếp vẫn trông như:
${val}
Sau đó, nó chỉ pgợi ý các mục ngăn xếp hàng đầu.
Vì vậy, đối với vấn đề của bạn:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
Nhưng:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
Tại sao không dùng cũ, tốt expr?
Cú pháp ví dụ:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
Đối với các biểu thức đúng , mã thoát expr là 0, với chuỗi '1' được gửi đến thiết bị xuất chuẩn. Đảo ngược cho các biểu thức sai .
Tôi đã kiểm tra điều này với expr GNU và FreeBSD 8.
expr 1.09 '<' -1.1sẽ in 1và thoát với 0(thành công).
Để kiểm tra xem hai số (có thể là phân số) có theo thứ tự hay không, sortcó hợp lý không:
min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
echo min is smallest
else
echo val is smallest
fi
Tuy nhiên, nếu bạn thực sự muốn giữ một giá trị tối thiểu được cập nhật, thì bạn không cần if. Sắp xếp các số và luôn luôn sử dụng số đầu tiên (ít nhất):
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
Thông thường tôi làm những điều tương tự với mã python nhúng:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
0.5và0.06). Bạn nên sử dụng một công cụ đã hiểu ký hiệu thập phân.