ব্যাশে ঠিক 2 টি উল্লেখযোগ্য অঙ্ক সহ ভাসমান পয়েন্ট সংখ্যাটি কীভাবে বিন্যাস করবেন?


17

আমি ব্যাশে ঠিক দুটি উল্লেখযোগ্য অঙ্ক সহ ভাসমান পয়েন্ট নম্বরটি মুদ্রণ করতে চাই (সম্ভবত একটি সাধারণ সরঞ্জাম যেমন অ্যাডাব্লু, বিসি, ডিসি, পার্ল ইত্যাদি ব্যবহার করে)।

উদাহরণ:

  • 76543 76000 হিসাবে মুদ্রিত করা উচিত
  • 0.0076543 মুদ্রিত হওয়া উচিত 0.0076 হিসাবে

উভয় ক্ষেত্রেই উল্লেখযোগ্য সংখ্যাগুলি 7 এবং 6 হয় similar একই ধরণের সমস্যার জন্য আমি কিছু উত্তর পড়েছি যেমন:

কিভাবে শেল মধ্যে ভাসমান পয়েন্ট সংখ্যা বৃত্তাকার?

ভাসমান পয়েন্ট ভেরিয়েবলের স্পষ্টতা সীমাবদ্ধ করে রাখুন Bash

তবে উত্তরগুলি উল্লেখযোগ্য সংখ্যার পরিবর্তে দশমিক জায়গাগুলির সংখ্যা (যেমন bcকমান্ড scale=2বা printfকমান্ড সহ %.2f) সীমাবদ্ধ করার উপর দৃষ্টি নিবদ্ধ করে ।

ঠিক 2 টি উল্লেখযোগ্য অঙ্ক সহ সংখ্যাটি ফর্ম্যাট করার সহজ উপায় কি আমার নিজের ফাংশনটি লিখতে হবে?

উত্তর:


13

প্রথম লিঙ্কযুক্ত প্রশ্নের এই উত্তরটির শেষে প্রায় ছুড়ে যাওয়ার লাইন রয়েছে:

%gউল্লেখযোগ্য সংখ্যার নির্দিষ্ট সংখ্যায় গোল করে দেখার জন্যও দেখুন ।

সুতরাং আপনি সহজভাবে লিখতে পারেন

printf "%.2g" "$n"

(তবে নীচের অংশটি দশমিক বিভাজক এবং লোকেলের উপর দেখুন এবং নোট-ন্যাশ printfসমর্থন করার দরকার নেই %fএবং দেখুন %g)।

উদাহরণ:

$ printf "%.2g\n" 76543 0.0076543
7.7e+04
0.0077

অবশ্যই, আপনার এখন খাঁটি দশমিকের পরিবর্তে ম্যান্টিসা-এক্সপোনেন্ট উপস্থাপনা রয়েছে, তাই আপনি ফিরে রূপান্তর করতে চাইবেন:

$ printf "%0.f\n" 7.7e+06
7700000

$ printf "%0.7f\n" 7.7e-06
0.0000077

এই সমস্ত একসাথে রাখা, এবং একটি ফাংশন এ মোড়ানো:

# Function round(precision, number)
round() {
    n=$(printf "%.${1}g" "$2")
    if [ "$n" != "${n#*e}" ]
    then
        f="${n##*e-}"
        test "$n" = "$f" && f= || f=$(( ${f#0}+$1-1 ))
        printf "%0.${f}f" "$n"
    else
        printf "%s" "$n"
    fi
}

(নোট - এই ফাংশন পোর্টেবল (POSIX) শেল লেখা আছে, কিন্তু যে অনুমান printf। হ্যান্ডলগুলি ফ্লোটিং পয়েন্ট ধর্মান্তর ব্যাশ হয়েছে একটি বিল্ট-ইন printfকরে করে, আপনি এখন করছি ঠিক আছে এখানে, এবং গনুহ বাস্তবায়ন এছাড়াও কাজ করে, তাই সবচেয়ে গনুহ / লিনাক্স সিস্টেমগুলি নিরাপদে ড্যাশ ব্যবহার করতে পারে)।

পরীক্ষার মামলা

radix=$(printf %.1f 0)
for i in $(seq 12 | sed -e 's/.*/dc -e "12k 1.234 10 & 6 -^*p"/e' -e "y/_._/$radix/")
do
    echo $i "->" $(round 2 $i)
done

পরীক্ষার ফলাফল

.000012340000 -> 0.000012
.000123400000 -> 0.00012
.001234000000 -> 0.0012
.012340000000 -> 0.012
.123400000000 -> 0.12
1.234 -> 1.2
12.340 -> 12
123.400 -> 120
1234.000 -> 1200
12340.000 -> 12000
123400.000 -> 120000
1234000.000 -> 1200000

দশমিক বিভাজক এবং লোকালে একটি নোট

উপরের সমস্ত কাজ করে ধরে নেওয়া হয়েছে যে বেশিরভাগ ইংরেজী স্থানীয় লোকের মতোই মূলত অক্ষর (দশমিক বিভাজক হিসাবেও পরিচিত) is পরিবর্তে .অন্যান্য লোকেল ব্যবহার ,করে এবং কয়েকটি শেলের একটি বিল্ট-ইন রয়েছে printfযা লোকালকে সম্মান করে। এই শেলগুলিতে আপনাকে রেডিক্স চরিত্র হিসাবে LC_NUMERIC=Cবাধ্যতামূলকভাবে সেট করতে .বা /usr/bin/printfঅন্তর্নির্মিত সংস্করণটির ব্যবহার রোধ করতে লিখতে হবে। এই আধুনিকটি জটিল যে জটিল (জটিলতার সাথে কমপক্ষে কিছু সংস্করণ) সর্বদা ব্যবহার করে যুক্তিগুলি বিশ্লেষণ .করে তবে বর্তমান লোকাল সেটিংস ব্যবহার করে মুদ্রণ করে।


@ স্টাফেন চেজেলাস, আপনি বাশিজম সরিয়ে দেওয়ার পরে আপনি কেন আমার সাবধানে পরীক্ষিত পসিক্স শেল শেবাংকে বাশে ফিরিয়েছিলেন? আপনার মন্তব্যে উল্লেখ আছে %f/ %g, তবে এটি printfতর্ক and এবং printfএকটি পসিক্স শেল রাখার জন্য একটি পসিক্সের প্রয়োজন হয় না । আমি মনে করি সেখানে সম্পাদনার পরিবর্তে আপনার মন্তব্য করা উচিত ছিল।
টবি স্পিড

printf %gপসিক্স স্ক্রিপ্টে ব্যবহার করা যাবে না। এটি সত্য যে এটি printfইউটিলিটির নিচে রয়েছে , তবে সেই ইউটিলিটি বেশিরভাগ শেলগুলিতে অন্তর্নির্মিত। ওপকে ব্যাশ হিসাবে ট্যাগ করা হয়েছে, সুতরাং ব্যাশ শেবাং ব্যবহার করা একটি মুদ্রক যে% g সমর্থন করে তা পাওয়ার সহজ উপায়। অন্যথায়, আপনার একটি প্রিন্টফ (বা সেখানে নির্মিত shযদি আপনার printfপ্রিন্টফ বিল্টইন) অ-মানক (তবে বেশ সাধারণ) সমর্থন করে %g...
স্টাফেন চ্যাজেলাস

dashএর একটি বিল্টিন রয়েছে printf(যা সমর্থন করে %g)। জিএনইউ সিস্টেমে, mkshএই দিনগুলিতে সম্ভবত একমাত্র শেল যা কোনও বিল্টিন থাকবে না printf
স্টাফেন চেজেলাস

আপনার উন্নতির জন্য ধন্যবাদ - আমি কেবল শেবাং অপসারণ করতে সম্পাদনা করেছি (যেহেতু প্রশ্ন ট্যাগ করা হয়েছে bash) এবং এর কয়েকটি নোটগুলিতে প্রকাশ করতে হবে - এখনই কি এটি সঠিক দেখাচ্ছে?
টবি স্পিড

1
দুর্ভাগ্যক্রমে এটি যদি অঙ্কের সঠিক সংখ্যাটি শূন্য হয় তবে এটি অঙ্কের সঠিক সংখ্যাটি মুদ্রণ করে না। উদাহরণস্বরূপ printf "%.3g\n" 0.4000.4 নয় 0.400 দেয়
ফিরস্কি

4

টি এল; ডিআর

কেবল sigfবিভাগে ফাংশনটি অনুলিপি করুন এবং ব্যবহার করুন A reasonably good "significant numbers" function:ড্যাশ সহ কাজ করার জন্য এটি লিখিত (এই উত্তরের সমস্ত কোড হিসাবে) ।

এটা তোলে দেব printfকরার পড়তা এন এর পূর্ণসংখ্যা অংশ দিয়ে $sigডিজিটের।

দশমিক বিভাজক সম্পর্কে।

প্রিন্টফের সাহায্যে সমাধানের প্রথম সমস্যাটি হ'ল "দশমিক চিহ্ন" এর প্রভাব এবং ব্যবহার যা মার্কিন যুক্তরাষ্ট্রে একটি পয়েন্ট এবং ডিই-তে একটি কমা (উদাহরণস্বরূপ)। এটি একটি সমস্যা কারণ কিছু লোকেলের (বা শেল) জন্য যা কাজ করে তা অন্য কোনও লোকেলের সাথে ব্যর্থ হবে। উদাহরণ:

$ dash -c 'printf "%2.3f\n" 12.3045'
12.305
$  ksh -c 'printf "%2.3f\n" 12.3045'
ksh: printf: 12.3045: arithmetic syntax error
ksh: printf: 12.3045: arithmetic syntax error
ksh: printf: warning: invalid argument of type f
12,000
$ ksh -c 'printf "%2.2f\n" 12,3045'
12,304

একটি সাধারণ (এবং ভুল সমাধান) হ'ল LC_ALL=Cপ্রিন্টফ কমান্ডের জন্য সেট করা । তবে এটি দশমিক চিহ্নটিকে একটি নির্দিষ্ট দশমিক পয়েন্টে সেট করে। লোকালগুলির জন্য যেখানে কমা (বা অন্যান্য) সাধারণ ব্যবহৃত চরিত্র যা একটি সমস্যা that

সমাধানটি হ'ল লোকেল দশমিক বিভাজকটি কী তা চালনা করে তা চালানোর জন্য স্ক্রিপ্টের ভিতরে খুঁজে বের করা। এটি বেশ সহজ:

$ printf '%1.1f' 0
0,0                            # for a comma locale (or shell).

শূন্যগুলি সরানো হচ্ছে:

$ dec="$(IFS=0; printf '%s' $(printf '%.1f'))"; echo "$dec"
,                              # for a comma locale (or shell).

সেই মানটি পরীক্ষার তালিকার সাথে ফাইলটি পরিবর্তন করতে ব্যবহৃত হয়:

sed -i 's/[,.]/'"$dec"'/g' infile

এটি কোনও শেল বা লোকালে রানগুলি স্বয়ংক্রিয়ভাবে বৈধ করে তোলে।


কিছু বেসিক।

বিন্যাসে %.*eবা এমনকি প্রিন্টফের সাথে ফর্ম্যাট করতে নম্বরটি কাটা স্বজ্ঞাত হওয়া উচিত %.*g। ব্যবহারের মধ্যে প্রধান পার্থক্য %.*eবা %.*gকীভাবে তারা অঙ্কগুলি গণনা করে। একটিতে পূর্ণ গণনা ব্যবহার করা হয়, অন্যটির গণনা কম 1:

$ printf '%.*e  %.*g' $((4-1)) 1,23456e0 4 1,23456e0
1,235e+00  1,235

এটি 4 টি গুরুত্বপূর্ণ অঙ্কের জন্য ভাল কাজ করেছে।

সংখ্যাটি সংখ্যা থেকে কাটা যাওয়ার পরে, 0 থেকে আলাদা (যেমন এটি উপরে ছিল) আলাদা করে সংখ্যার ফর্ম্যাট করতে আমাদের একটি অতিরিক্ত পদক্ষেপের প্রয়োজন।

$ N=$(printf '%.*e' $((4-1)) 1,23456e3); echo "$N"
1,235e+03
$ printf '%4.0f' "$N"
1235

এটি সঠিকভাবে কাজ করে। পূর্ণসংখ্যার অংশের গণনা (দশমিক চিহ্নের বাম দিকে) ব্যয়কারীর মান ($ এক্সপ্রেস) is দশমিক সংখ্যার গণনা হ'ল দশমিক বিভাজকের বাম অংশে ইতিমধ্যে ব্যবহৃত অঙ্কের পরিমাণের পরিমাণ ($ সিগ) কম:

a=$((exp<0?0:exp))                      ### count of integer characters.
b=$((exp<sig?sig-exp:0))                ### count of decimal characters.
printf '%*.*f' "$a" "$b" "$N"

যেহেতু fবিন্যাসের অবিচ্ছেদ্য অংশটির কোনও সীমা নেই, প্রকৃতপক্ষে এটি স্পষ্টভাবে ঘোষণা করার দরকার নেই এবং এটি (সরল) কোডটি কার্যকর করে:

a=$((exp<sig?sig-exp:0))                ### count of decimal characters.
printf '%0.*f' "$a" "$N"

প্রথম বিচার।

একটি প্রথম ফাংশন যা আরও স্বয়ংক্রিয় পদ্ধতিতে এটি করতে পারে:

# Function significant (number, precision)
sig1(){
    sig=$(($2>0?$2:1))                      ### significant digits (>0)
    N=$(printf "%0.*e" "$(($sig-1))" "$1")  ### N in sci (cut to $sig digits).
    exp=$(echo "${N##*[eE+]}+1"|bc)         ### get the exponent.
    a="$((exp<sig?sig-exp:0))"              ### calc number of decimals.
    printf "%0.*f" "$a" "$N"                ### re-format number.
}

এই প্রথম প্রচেষ্টাটি বহু সংখ্যার সাথে কাজ করে তবে এমন সংখ্যায় ব্যর্থ হবে যার জন্য উপলভ্য অঙ্কগুলির পরিমাণ উল্লেখযোগ্য গণনার চেয়ে কম এবং ব্যয়কারী -4 এর চেয়ে কম:

   Number       sig                       Result        Correct?
   123456789 --> 4<                       123500000 >--| yes
       23455 --> 4<                           23460 >--| yes
       23465 --> 4<                           23460 >--| yes
      1,2e-5 --> 6<                    0,0000120000 >--| no
     1,2e-15 -->15< 0,00000000000000120000000000000 >--| no
          12 --> 6<                         12,0000 >--| no  

এটি অনেকগুলি শূন্য যুক্ত করবে যাগুলির প্রয়োজন নেই।

দ্বিতীয় বিচার।

এটি সমাধান করার জন্য আমাদের ঘেরের N এবং পরিষ্কারের কোনও জিরো দরকার to তারপরে আমরা উপলব্ধ অঙ্কগুলির কার্যকর দৈর্ঘ্য পেতে পারি এবং এটিতে কাজ করতে পারি:

# Function significant (number, precision)
sig2(){ local sig N exp n len a
    sig=$(($2>0?$2:1))                      ### significant digits (>0)
    N=$(printf "%+0.*e" "$(($sig-1))" "$1") ### N in sci (cut to $sig digits).
    exp=$(echo "${N##*[eE+]}+1"|bc)         ### get the exponent.
    n=${N%%[Ee]*}                           ### remove sign (first character).
    n=${n%"${n##*[!0]}"}                    ### remove all trailing zeros
    len=$(( ${#n}-2 ))                      ### len of N (less sign and dec).
    len=$((len<sig?len:sig))                ### select the minimum.
    a="$((exp<len?len-exp:0))"              ### use $len to count decimals.
    printf "%0.*f" "$a" "$N"                ### re-format the number.
}

যাইহোক, এটি ভাসমান পয়েন্ট গণিত ব্যবহার করছে, এবং "ভাসমান পয়েন্টে কিছুই সহজ নয়": আমার সংখ্যাগুলি কেন যোগ হচ্ছে না?

তবে "ভাসমান পয়েন্ট" এর কিছুই সহজ নয়।

printf "%.2g  " 76500,00001 76500
7,7e+04  7,6e+04

যাহোক:

 printf "%.2g  " 75500,00001 75500
 7,6e+04  7,6e+04

কেন ?:

printf "%.32g\n" 76500,00001e30 76500e30
7,6500000010000000001207515928855e+34
7,6499999999999999997831226199114e+34

এবং, এছাড়াও, কমান্ডটি printfঅনেকগুলি শেলের একটি অন্তর্নির্মিত। শেল দিয়ে
কী printfমুদ্রণ পরিবর্তন হতে পারে:

$ dash -c 'printf "%.*f" 4 123456e+25'
1234560000000000020450486779904.0000
$  ksh -c 'printf "%.*f" 4 123456e+25'
1234559999999999999886313162278,3840

$  dash ./script.sh
   123456789 --> 4<                       123500000 >--| yes
       23455 --> 4<                           23460 >--| yes
       23465 --> 4<                           23460 >--| yes
      1.2e-5 --> 6<                        0.000012 >--| yes
     1.2e-15 -->15<              0.0000000000000012 >--| yes
          12 --> 6<                              12 >--| yes
  123456e+25 --> 4< 1234999999999999958410892148736 >--| no

একটি যুক্তিসঙ্গতভাবে ভাল "উল্লেখযোগ্য সংখ্যা" ফাংশন:

dec=$(IFS=0; printf '%s' $(printf '%.1f'))   ### What is the decimal separator?.
sed -i 's/[,.]/'"$dec"'/g' infile

zeros(){ # create an string of $1 zeros (for $1 positive or zero).
         printf '%.*d' $(( $1>0?$1:0 )) 0
       }

# Function significant (number, precision)
sigf(){ local sig sci exp N sgn len z1 z2 b c
    sig=$(($2>0?$2:1))                      ### significant digits (>0)
    N=$(printf '%+e\n' $1)                  ### use scientific format.
    exp=$(echo "${N##*[eE+]}+1"|bc)         ### find ceiling{log(N)}.
    N=${N%%[eE]*}                           ### cut after `e` or `E`.
    sgn=${N%%"${N#-}"}                      ### keep the sign (if any).
    N=${N#[+-]}                             ### remove the sign
    N=${N%[!0-9]*}${N#??}                   ### remove the $dec
    N=${N#"${N%%[!0]*}"}                    ### remove all leading zeros
    N=${N%"${N##*[!0]}"}                    ### remove all trailing zeros
    len=$((${#N}<sig?${#N}:sig))            ### count of selected characters.
    N=$(printf '%0.*s' "$len" "$N")         ### use the first $len characters.

    result="$N"

    # add the decimal separator or lead zeros or trail zeros.
    if   [ "$exp" -gt 0 ] && [ "$exp" -lt "$len" ]; then
            b=$(printf '%0.*s' "$exp" "$result")
            c=${result#"$b"}
            result="$b$dec$c"
    elif [ "$exp" -le 0 ]; then
            # fill front with leading zeros ($exp length).
            z1="$(zeros "$((-exp))")"
            result="0$dec$z1$result"
    elif [ "$exp" -ge "$len" ]; then
            # fill back with trailing zeros.
            z2=$(zeros "$((exp-len))")
            result="$result$z2"
    fi
    # place the sign back.
    printf '%s' "$sgn$result"
}

এবং ফলাফলগুলি হ'ল:

$ dash ./script.sh
       123456789 --> 4<                       123400000 >--| yes
           23455 --> 4<                           23450 >--| yes
           23465 --> 4<                           23460 >--| yes
          1.2e-5 --> 6<                        0.000012 >--| yes
         1.2e-15 -->15<              0.0000000000000012 >--| yes
              12 --> 6<                              12 >--| yes
      123456e+25 --> 4< 1234000000000000000000000000000 >--| yes
      123456e-25 --> 4<       0.00000000000000000001234 >--| yes
 -12345.61234e-3 --> 4<                          -12.34 >--| yes
 -1.234561234e-3 --> 4<                       -0.001234 >--| yes
           76543 --> 2<                           76000 >--| yes
          -76543 --> 2<                          -76000 >--| yes
          123456 --> 4<                          123400 >--| yes
           12345 --> 4<                           12340 >--| yes
            1234 --> 4<                            1234 >--| yes
           123.4 --> 4<                           123.4 >--| yes
       12.345678 --> 4<                           12.34 >--| yes
      1.23456789 --> 4<                           1.234 >--| yes
    0.1234555646 --> 4<                          0.1234 >--| yes
       0.0076543 --> 2<                          0.0076 >--| yes
   .000000123400 --> 2<                      0.00000012 >--| yes
   .000001234000 --> 2<                       0.0000012 >--| yes
   .000012340000 --> 2<                        0.000012 >--| yes
   .000123400000 --> 2<                         0.00012 >--| yes
   .001234000000 --> 2<                          0.0012 >--| yes
   .012340000000 --> 2<                           0.012 >--| yes
   .123400000000 --> 2<                            0.12 >--| yes
           1.234 --> 2<                             1.2 >--| yes
          12.340 --> 2<                              12 >--| yes
         123.400 --> 2<                             120 >--| yes
        1234.000 --> 2<                            1200 >--| yes
       12340.000 --> 2<                           12000 >--| yes
      123400.000 --> 2<                          120000 >--| yes

0

যদি আপনার কাছে ইতিমধ্যে স্ট্রিং হিসাবে সংখ্যাটি রয়েছে, অর্থাৎ "3456" বা "0.003756", তবে আপনি কেবল স্ট্রিং ম্যানিপুলেশন ব্যবহার করে এটি করতে পারেন। নীচে আমার মাথার উপরের অংশটি বন্ধ রয়েছে এবং পুরোপুরি পরীক্ষা করা হয়নি এবং সেড ব্যবহার করা হয়েছে তবে বিবেচনা করুন:

f() {
    local A="$1"
    local B="$(echo "$A" | sed -E "s/^-?0?\.?0*//")"
    local C="$(eval echo "${A%$B}")"
    if ((${#B} > 2)); then
        D="${B:0:2}"
    else
        D="$B"
    fi
    echo "$C$D"
}

মূলত যেখানে আপনি শুরু করে কোনও "-0.000" স্টাফ সরিয়ে ফেলে থাকেন এবং তারপরে বাকী অংশে একটি সাধারণ স্ট্রাস্টিং অপারেশন ব্যবহার করুন। উপরোক্ত বিষয়ে একটি সতর্কতা হ'ল একাধিক শীর্ষস্থানীয় 0 টি সরানো হয়নি। আমি এটি অনুশীলন হিসাবে ছেড়ে দেব।


1
অনুশীলনের চেয়ে বেশি: এটি শূন্যের সাথে পূর্ণসংখ্যাকে প্যাড করে না, বা এটি এম্বেডড দশমিক পয়েন্টের জন্য অ্যাকাউন্ট করে না। তবে হ্যাঁ, এই পদ্ধতির সাহায্যে এটি করণীয় (যদিও এটি অর্জন অপের দক্ষতার বাইরেও হতে পারে)।
টমাস ডিকি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.