Cách so sánh với số dấu phẩy động trong tập lệnh shell


22

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:


5

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

4
Điều này không thể được sửa chữa mà không có nhiều công việc (thử so sánh 0.50.06). Bạn nên sử dụng một công cụ đã hiểu ký hiệu thập phân.
Gilles 'SO- ngừng trở nên xấu xa'

Cảm ơn Gilles, đã cập nhật nó để hoạt động chung hơn phiên bản trước.
ata

Lưu ý rằng nó nói rằng 1.00000000000000000000000001lớn hơn 2.
Stéphane Chazelas

Stéphane đã đúng. Đó là vì giới hạn bit trong biểu diễn số của bash. Tất nhiên, nếu bạn muốn chịu đựng nhiều đau khổ hơn, bạn có thể sử dụng đại diện của chính mình .... :)
ata

35

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.


12

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ụ.

  • R / Rupcript (hệ thống đồ họa và tính toán thống kê GNU R)
  • quãng tám (tương thích với Matlab)
  • bc (Ngôn ngữ máy tính chính xác tùy ý GNU bc)

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ả minval 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

10

Đố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àm việc trên các bảng số, hãy tìm R ( ví dụ ).


6

Sử dụng sắp xếp số

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

Hỗ trợ ký hiệu điện tử

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.

Có thể so sánh với vô cùng

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

Hoạt động so sánh

Để 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

Mẹo tốt! Tôi thực sự thích cái nhìn sâu sắc của bạn rằng kiểm tra 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
Dave

3

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
...

Lưu ý rằng tập lệnh ksh93 ở trên chỉ hoạt động ở các vị trí có dấu phân cách thập phân .(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 đề đó
Stéphane Chazelas

Thật vậy, trả lời chỉnh sửa để làm rõ điểm đó.
jlliagre

Đặt LC_NUMERIC sẽ không hoạt động nếu người dùng đã đặt 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.
Stéphane Chazelas

@ StéphaneChazelas đã khắc phục sự cố LC_NUMERIC. Với cú pháp tập lệnh OP, .dù sao tôi cũng cho rằng dấu phân cách ưa thích của anh ta .
jlliagre

Đúng, nhưng đó là miền địa phương của người sử dụng tập lệnh, không phải là miền địa phương của tác giả kịch bản. Là một tác giả kịch bản, bạn nên tính đến nội địa hóa và các tác dụng phụ của nó.
Stéphane Chazelas 23/07/14

1
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 adtă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

0

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.


GNU expr chỉ hỗ trợ so sánh số học trên các số nguyên. Ví dụ của bạn sử dụng so sánh từ điển sẽ thất bại trên các số âm. Ví dụ, expr 1.09 '<' -1.1sẽ in 1và thoát với 0(thành công).
Adrian Günter

0

Để 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

0

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

-1
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13

3
Bạn có thể vui lòng bình luận câu trả lời của bạn và thêm một số giải thích
Romeo Ninov
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.