शेल स्क्रिप्ट में फ्लोटिंग पॉइंट संख्या की तुलना कैसे करें


22

मैं एक शेल स्क्रिप्ट में दो फ्लोटिंग पॉइंट नंबरों की तुलना करना चाहता हूं। निम्नलिखित कोड काम नहीं कर रहा है:

#!/bin/bash   
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo $min 

जवाबों:


5

आप पूर्णांक और आंशिक भागों को अलग से देख सकते हैं:

#!/bin/bash
min=12.45
val=12.35    
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then    
    min=$val
fi
echo $min

जैसा कि टिप्पणी में फ़्रेड कहता है, यह तभी काम करता है जब दोनों नंबरों में आंशिक भाग हों और दोनों भागों में समान संख्या में अंक हों। यहाँ एक संस्करण है जो पूर्णांक या आंशिक और किसी भी बैश ऑपरेटर के लिए काम करता है:

#!/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
यह बहुत काम के बिना तय नहीं किया जा सकता है (तुलना 0.5और प्रयास करें 0.06)। आप बेहतर ढंग से एक उपकरण का उपयोग करेंगे जो पहले से ही दशमलव संकेतन को समझता है।
गिल्स एसओ- बुराई को रोकना '

धन्यवाद गिल्स, इसे पहले के संस्करण की तुलना में अधिक सामान्यतः काम करने के लिए अद्यतन किया गया।
ata

ध्यान दें कि यह कहता है कि 1.00000000000000000000000001इससे अधिक है 2
स्टीफन चेजलस 12

स्टीफन सही है। यह बैश की संख्या प्रतिनिधित्व में बिट सीमा के कारण ऐसा है। बेशक, यदि आप अधिक पीड़ित चाहते हैं तो आप अपने स्वयं के प्रतिनिधित्व का उपयोग कर सकते हैं .... :)
ata

35

बैश फ्लोटिंग पॉइंट अंकगणित को नहीं समझता है। यह एक दशमलव बिंदु वाले संख्याओं को तार के रूप में मानता है।

इसके बजाय awk या bc का उपयोग करें।

#!/bin/bash

min=12.45
val=10.35

if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then  
    min=${val}
fi

echo "$min"

यदि आप बहुत सारे गणित संचालन करने का इरादा रखते हैं, तो संभवतः अजगर या पर्ल पर भरोसा करना बेहतर है।


12

आप सरल जोड़तोड़ के लिए पैकेज संख्या-बर्तन का उपयोग कर सकते हैं ...

अधिक गंभीर गणित के लिए, इस लिंक को देखें ... यह कई विकल्पों का वर्णन करता है, जैसे।

  • आर / रुस्क्रिप्ट (GNU R सांख्यिकीय संगणना और ग्राफिक्स प्रणाली)
  • सप्तक (ज्यादातर मतलब संगत)
  • बीसी (जीएनयू बीसी मनमानी सटीक कैलकुलेटर भाषा)

का एक उदाहरण 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.

यहाँ एक bashहैक है ... यह एक स्ट्रिंग को बाएं से दाएं तुलना को सार्थक बनाने के लिए पूर्णांक में अग्रणी 0 जोड़ता है। कोड के इस विशेष टुकड़े के लिए आवश्यक है कि दोनों मिनट और वैल में वास्तव में दशमलव बिंदु हो और कम से कम एक दशमलव अंक हो।

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

उत्पादन:

min=10.35

10

फ्लोटिंग पॉइंट नंबरों (+ - * / और तुलना) पर सरल गणना के लिए, आप awk का उपयोग कर सकते हैं।

min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')

या, यदि आपके पास ksh93 या zsh (बैश नहीं) है, तो आप अपने शेल के बिल्ट-इन अंकगणित का उपयोग कर सकते हैं, जो फ्लोटिंग पॉइंट नंबरों का समर्थन करता है।

if ((min>val)); then ((val=min)); fi

अधिक उन्नत फ्लोटिंग पॉइंट गणना के लिए, ई.पू. देखें । यह वास्तव में मनमाने ढंग से सटीक निर्धारण संख्या पर काम करता है।

संख्याओं की तालिकाओं पर काम करने के लिए, R ( उदाहरण ) देखें।


6

संख्यात्मक प्रकार का उपयोग करें

कमांड sortमें एक विकल्प -g( --general-numeric-sort) होता है, जिसका उपयोग न्यूनतम या अधिकतम खोज कर <, "कम से कम" या >"से बड़ा" के रूप में किया जा सकता है ।

ये उदाहरण न्यूनतम पा रहे हैं:

$ printf '12.45\n10.35\n' | sort -g | head -1
10.35

ई-संकेतन का समर्थन करता है

यह ई-संकेतन के साथ फ्लोटिंग पॉइंट नंबरों के सामान्य सामान्य अंकन के साथ काम करता है

$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10

ध्यान दें , वास्तव में से कम, E-10पहली संख्या बना रही 0.000000001245है 10.35

अनंत से तुलना कर सकते हैं

अस्थायी बिंदु मानक, IEEE754 , कुछ विशेष मूल्यों को परिभाषित करता है। इन तुलनाओं के लिए, दिलचस्प INFअनंत के लिए हैं। नकारात्मक अनंतता भी है; दोनों मानक में अच्छी तरह से परिभाषित मूल्य हैं।

$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF

के sort -grबजाय अधिकतम उपयोग खोजने के लिए sort -g, क्रम को उलट कर:

$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45

तुलना संचालन

<तुलना करने के लिए ("कम से कम") को लागू करने के लिए , इसलिए इसका उपयोग ifआदि में किया जा सकता है , न्यूनतम मूल्यों में से एक से तुलना करें। यदि पाठ की तुलना में न्यूनतम मान के बराबर है , तो यह अन्य मूल्य से कम है:

$ 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)करना उसी के समान है a <= b। यह ध्यान देने योग्य है कि यह कड़ाई से कम के लिए जाँच नहीं करता है। यदि आप ऐसा करना चाहते हैं, तो आपको a == min(a, b) && a != max(a, b)अन्य पासवर्डa <= b and not a >= b
डेव

3

बस का उपयोग करें ksh( ksh93ठीक) या zsh, जो दोनों मूल रूप से फ्लोटिंग पॉइंट अंकगणित का समर्थन करते हैं:

$ cat test.ksh
#!/bin/ksh 
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo "$min"
$ ./test.ksh
10.35

संपादित करें: क्षमा करें, मुझे याद है ksh93कि पहले से ही सुझाव दिया गया था। मेरा जवाब सिर्फ यह रखने के लिए कि प्रारंभिक प्रश्न में पोस्ट की गई स्क्रिप्ट को खोल स्विच के बाहर कोई बदलाव नहीं किया जा सकता है।

Edit2: ध्यान दें कि ksh93वैरिएबल कंटेंट को आपके लोकेल के साथ संगत होना चाहिए, यानी फ्रेंच लोकेल के साथ, डॉट के बजाय कॉमा का उपयोग किया जाना चाहिए:

...
min=12,45
val=10,35
...

एक और अधिक मजबूत समाधान स्क्रिप्ट की शुरुआत में लोकेल सेट करना है, यह सुनिश्चित करने के लिए कि यह उपयोगकर्ता के लोकेल की परवाह किए बिना काम करेगा:

...
export LC_ALL=C
min=12.45
val=10.35
...

ध्यान दें कि उपरोक्त ksh93 स्क्रिप्ट केवल उन स्थानों पर काम करती है जहाँ दशमलव विभाजक है .(इसलिए आधी दुनिया में जहाँ दशमलव विभाजक नहीं है ,)। zshयह मुद्दा नहीं है।
स्टीफन चेजलस

वास्तव में, उस बिंदु को स्पष्ट करने के लिए संपादित उत्तर।
जुलियाग्रे

यदि उपयोगकर्ता ने सेट किया है LC_ALL, तो LC_NUMERIC काम नहीं करेगा , इसका मतलब यह भी है कि नंबर उपयोगकर्ता के पसंदीदा प्रारूप में प्रदर्शित नहीं होंगे (या इनपुट)। संभावित बेहतर दृष्टिकोण के लिए unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/… देखें ।
स्टीफन चेजलस

@ StéphaneChazelas ने LC_NUMERIC समस्या तय की। ओपी स्क्रिप्ट के सिंटैक्स को देखते हुए, मैं मान रहा हूं कि उनका पसंदीदा विभाजक .वैसे भी है।
jlliagre

हां, लेकिन यह स्क्रिप्ट उपयोगकर्ता का स्थान है, स्क्रिप्ट लेखक का स्थान नहीं जो मायने रखता है। एक पटकथा लेखक के रूप में, आपको स्थानीयकरण और इसके दुष्प्रभावों को ध्यान में रखना चाहिए।
स्टीफन चेज़लस

1
min=$(echo "${min}sa ${val}d la <a p" | dc)

का उपयोग करता है dcकरने के लिए कैलकुलेटर sके लिए मूल्य फाड़े $minरजिस्टर में aऔर dका मूल्य uplicates $valअपने मुख्य निष्पादन ढेर के शीर्ष पर। इसके बाद स्टैक के शीर्ष पर lसामग्री होती है a, जिस बिंदु पर यह दिखता है:

${min} ${val} ${val}

<ढेर के बंद शीर्ष दो प्रविष्टियों पॉप और उन्हें तुलना करती है। तो स्टैक तब दिखता है:

${val}

यदि शीर्ष प्रविष्टि दूसरे से शीर्ष पर कम थी तो यह शीर्ष aपर की सामग्री को धक्का देता है , इसलिए स्टैक ऐसा दिखता है:

${min} ${val}

इसके अलावा यह कुछ भी नहीं करता है और स्टैक अभी भी दिखता है:

${val} 

फिर यह सिर्फ pशीर्ष स्टैक प्रविष्टि पर हस्ताक्षर करता है।

तो आपकी समस्या के लिए:

min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.35

परंतु:

min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.45

0

पुराने, अच्छे का उपयोग क्यों न करें expr?

उदाहरण वाक्य रचना:

if expr 1.09 '>' 1.1 1>/dev/null; then
    echo 'not greater'
fi

के लिए सही अभिव्यक्ति, expr बाहर निकलें कोड 0 है, स्ट्रिंग '1' stdout करने के लिए भेजा है। झूठे भावों के लिए उलटा ।

मैंने जीएनयू और फ्रीबीएसडी 8 एक्सपायर के साथ इसकी जांच की है।


GNU एक्सपर्स केवल पूर्णांक पर अंकगणितीय तुलना का समर्थन करता है। आपका उदाहरण लेक्सिकोग्राफ़िक तुलना का उपयोग करता है जो नकारात्मक संख्याओं पर विफल होगा। उदाहरण के लिए, (सफलता) के साथ expr 1.09 '<' -1.1प्रिंट 1और बाहर निकलेगा 0
एड्रियन गुंटर

0

यह जांचने के लिए कि क्या दो (संभवतः आंशिक) क्रम में हैं, sort(यथोचित) पोर्टेबल:

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

हालाँकि, यदि आप वास्तव में न्यूनतम मूल्य को अपडेट रखना चाहते हैं, तो आपको इसकी आवश्यकता नहीं है if। संख्याओं को क्रमबद्ध करें, और हमेशा पहले (कम से कम) एक का उपयोग करें:

min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest

0

आमतौर पर मैं एम्बेडेड पायथन कोड के साथ ऐसी ही चीजें करता हूं:

#!/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
क्या आप अपना उत्तर बता सकते हैं और कुछ स्पष्टीकरण जोड़ सकते हैं
रोमियो निनोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.