Bash bc kullanarak ondalık ondalık nasıl?


45

Bash komut dosyası kullanarak ne istediğime hızlı bir örnek:

#!/bin/bash
echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
echo "scale=2; $float/1.18" |bc -l
read -p "Press any key to continue..."
bash scriptname.sh

Fiyatı varsayalım: 48.86 Cevap şu olacaktır: 41.406779661 (41.40 aslında kullanıyorum çünkü scale=2;)

Benim Soru şu: Ben ikinci ondalık yuvarlamak nasıl bu şekilde cevap göstermek için ?: 41.41


Garip buluyorum çünkü "printf"% 0.2f \ n "41.445" şimdi çalışıyor ancak "printf"% 0.2f \ n "41.435 ve printf"% 0.2f \ n "41.455" yapıyor. Kendi davanız bile işe yarıyor (On 12.04'te) ancak .445
Luis Alvarado'da

8
IMHO, hiç kimse bu soruyu tatmin edici bir şekilde cevaplamadı , belki de bctalep edileni (ya da en azından bu yazıyı bulduğumda sorduğum soruyu), ki bu sayede onlardan kaçınmayıbc (bunun adıyla bash) kullanılacağını başaramadı .
Adam Katz

Bu sorunun eski olduğunu biliyorum, ancak kendi bc uygulamamı zorlayan bir yorumuma dayanamıyorum : github.com/gavinhoward/bc . Bunun için, yerleşik kütüphanede r()işleviyle birlikte kullanabilirsiniz bc -le "r($float/1.18, 2)".
Gavin Howard

Yanıtlar:


31

Bir bash yuvarlak işlevi:

round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

Kod örneğinizde kullanılır:

#!/bin/bash
# the function "round()" was taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# the round function:
round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
#echo "scale=2; $float/1.18" |bc -l
echo $(round $float/1.18 2);
read -p "Press any key to continue..."

İyi şanslar: o)


1
btw, sayı negatifse kullanmak zorundayız-0.5
Kova Gücü

Test konsolu üzerinde çalışma örneği alınamadı! "Hata ayıklama" biraz, örneğin $2 = 3kullanmak zorunda kaldığımı ortaya çıkardı echo $(env printf %.3f $(echo "scale=3;((1000*$1)+0.5)/1000" | bc)). Zihin env printf önce! Bu, size başka bir yerden kopyaladığınız şeyi anlamanızın her zaman önemli olduğunu size tekrar öğretir .
sözdizimi

@Aquarius Power ilham için teşekkürler! Şimdi hem negatif hem de pozitif sayılarla çalışacak olan yukarıdaki betiğin bir versiyonunu seçtim.
sözdizimi

Bu gereksiz yere karmaşık ve migas ve zuberuber tarafından sağlanan basit cevaplara ulaşmak için her türlü ekstra aritmetik yapar.
Adam Katz

@AquariusPower +1 evet, negatif sayılar için farklı. Negatif (tamsayı olmayan!) Bir sayıyı test etmek için biraz denedim ve if [ "$ 1/1" | bc` -gt 0] `- kontrol etmenin daha zarif bir yolu var mı (" - "için dizgiyi ayrıştırma hariç)?
RobertG

30

En basit çözüm:

printf %.2f $(echo "$float/1.18" | bc -l)

2
Yukarıdaki bir yorumda da belirtildiği gibi ( askubuntu.com/a/179949/512213 ), bu maalesef negatif sayılar için yanlış davranacaktı.
RobertG

2
@RobertG: Neden? Bu çözüm kullanmıyor +0.5. Deneyin printf "%.2f\n" "$(bc -l <<<"48.86/1.18")" "$(bc -l <<<"-48.86/1.18")"ve alacak 41.41ve -41.41.
musiphil

1
Pipo kullanarak da mümkündür:echo "$float/1.18" | bc -l | xargs printf %.2f
tatlı

Bu cevap çözüm bana verir: bash: printf: 1.69491525423728813559: invalid number, yerel ayara bağlı.
Torsten Bronger

19

Bash / awk yuvarlama:

echo "23.49" | awk '{printf("%d\n",$1 + 0.5)}'  

Python'unuz varsa, böyle bir şey kullanabilirsiniz:

echo "4.678923" | python -c "print round(float(raw_input()))"

Bahşiş için teşekkürler ama ihtiyacım olanı çözmüyor. Bunu sadece bash kullanarak yapmak zorundayım ...
blackedx

1
Python komutu, hızlı komut dosyaları için daha okunur ve iyidir. Ayrıca yuvarlak işlevine örneğin ", 3" ekleyerek isteğe bağlı basamak yuvarlamayı da destekler. Thanks
Elle

echo "$float" |awk '{printf "%.2f", $1/1.18}'Sorunun istenen matematiğini, yüzlerce kişinin talep edilen zaafına uygulayacaktır. Bu bc, söz konusu çağrı kadar "bash kullanmak" kadar .
Adam Katz

2
Python için sadece python -c "print(round($num))"nerede bir şey kullanabilirsiniz num=4.678923. Stdin ile uğraşmaya gerek yok. Ayrıca yuvarlak n basamaklı böylece hoşlanabilirim: python -c "print(round($num, $n))".
Altı

Ben bu çözüm ile oldukça düzgün bir şeyler yapmayı başardı, benim sorunum ben aşağıda ile geldi bu yüzden bu durumda hep yuvarlak ihtiyacım alamadım 0.5 idi: echo 5 | python -c "print int(round((float(raw_input())/2)-0.5))"(toplayıp ne zaman + - yuvarlak aşağı).
Yaron,

4

İşte tamamen bir bc çözümü. Yuvarlama kuralları: +/- 0.5'te sıfırdan uzağa yuvarlayın.

Aradığınız ölçeği $ result_scale; matematiğiniz, $ MATH'ın bc komut listesinde bulunduğu yerde olmalıdır:

bc <<MATH
h=0
scale=0

/* the magnitude of the result scale */
t=(10 ^ $result_scale)

/* work with an extra digit */
scale=$result_scale + 1

/* your math into var: m */
m=($MATH)

/* rounding and output */
if (m < 0) h=-0.5
if (m > 0) h=0.5

a=(m * t + h)

scale=$result_scale
a / t
MATH

1
çok ilginç! MATH=-0.34;result_scale=1;bc <<MATH, #ObjectiveAndClear: Burada açıklandığı gibi 5 :)
Kova Gücü

Bunu "en iyi cevap" için aday gösteriyorum.
Marc Tamsky

2

Bunun eski bir soru olduğunu biliyorum, ancak 'if' veya dalsız saf bir 'bc' çözümüm var:

#!/bin/sh
bcr()
{
    echo "scale=$2+1;t=$1;scale-=1;(t*10^scale+((t>0)-(t<0))/2)/10^scale" | bc -l
}

bcr '2/3' 5Veya bcr '0.666666' 2-> gibi kullanın (ifadenin ardından ölçek)

Bu mümkündür çünkü bc'de (C / C ++ gibi) hesaplamalarınızdaki mantıksal ifadeleri karıştırmanıza izin verilir. İfade ((t>0)-(t<0))/2), 't' işaretine bağlı olarak +/- 0.5 olarak değerlendirilir ve bu nedenle yuvarlama için doğru değeri kullanır.


Bu düzgün görünüyor, ama yerine bcr "5 / 2" 0döner . Bir şey mi eksik? 23
blujay

1
#!/bin/bash
# - loosely based on the function "round()", taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# - inspired by user85321 @ askubuntu.com (original author)
#   and Aquarius Power

# the round function (alternate approach):

round2()
{
    v=$1
    vorig=$v
    # if negative, negate value ...
    (( $(bc <<<"$v < 0") == 1 )) && v=$(bc <<<"$v * -1")
    r=$(bc <<<"scale=$3;(((10^$3)*$v/$2)+0.5)/(10^$3)")

    # ... however, since value was only negated to get correct rounding, we 
    # have to add the minus sign again for the resulting value ...

    (( $(bc <<< "$vorig < 0") == 1 )) && r=$(bc <<< "$r * -1")
    env printf %.$3f $r
};

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
round2 $float 1.18 2
echo && read -p "Press any key to continue..."

Aslında basittir: Negatif sayılar için açıkça kodlanmış bir "-0.5" değişkeni eklemenize gerek yoktur. Matematiksel olarak konuşursak, sadece argümanın mutlak değerini hesaplayacağız ve normalde yaptığımız gibi hala 0.5 ekleyeceğiz . Ancak (maalesef) elimizde yerleşik bir abs()fonksiyon bulunmadığı için (birini kodlamazsak ), eğer olumsuzsa bu argümanı basitçe yok edeceğiz .

Ayrıca, bölümle bir parametre olarak çalışmak çok zahmetli oldu (çünkü benim çözümüm için bölücüye ve bölene ayrı ayrı erişebilmeliyim). Bu yüzden betiğimde üçüncü bir parametre daha var.


son düzenlemelerinizle ilgili @muru: herestrings yerine echo .. |, shell aritmetik, ne anlama geliyor echo $()? Bunu açıklaması kolaydır: burada dizgileriniz her zaman yazılabilir bir/tmp dizin gerektirecektir ! Ve benim çözüm ayrıca, örneğin bir acil durum kök kabuğu salt okunur bir ortamda çalışacak /olan değil her zaman varsayılan olarak yazılabilir. Bu yüzden böyle kodlamamın iyi bir nedeni vardı.
sözdizimi,

Herestrings, aynı fikirdeyim. Ama ne zaman echo $()ihtiyaç vardır? Bu ve girinti düzenlemelerime neden oldu, sapmalar az önce oldu.
muru

@muru Eh, bunun en anlamsız vakası, echo $()düzeltilmek için gecikmiş bir durumdu , aslında sondan bir önceki dönem oldu lol. Söylediğin için teşekkürler. En azından bu şimdi daha iyi görünüyor, inkar edemem. Neyse, buradaki mantığı sevdim. Birçok boolean malzeme, "if" lerden daha az gereksizdir (ki bu kesinlikle kodu% 50 oranında düşürür).
sözdizimi,

1

Hala bcbir fonksiyon içerisinde sadece bir değerin nasıl yuvarlanacağına dair kesin bir cevap arıyorum , fakat işte size saf bir bashcevap:

#!/bin/bash

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"

embiggen() {
  local int precision fraction=""
  if [ "$1" != "${1#*.}" ]; then  # there is a decimal point
    fraction="${1#*.}"       # just the digits after the dot
  fi
  int="${1%.*}"              # the float as a truncated integer
  precision="${#fraction}"   # the number of fractional digits
  echo $(( 10**10 * $int$fraction / 10**$precision ))
}

# round down if negative
if [ "$float" != "${float#-}" ]
  then round="-5000000000"
  else round="5000000000"
fi

# calculate rounded answer (sans decimal point)
answer=$(( ( `embiggen $float` * 100 + $round ) / `embiggen 1.18` ))

int=${answer%??}  # the answer as a truncated integer

echo $int.${answer#$int}  # reassemble with correct precision

read -p "Press any key to continue..."

Temel olarak, bu, ondalık sayıları dikkatlice ayıklar, her şeyi 100 milyar (10¹⁰, 10**10inç bash) ile çarpar , hassasiyet ve yuvarlama için ayarlar, asıl bölmeyi gerçekleştirir, uygun büyüklüğe geri böler ve ardından ondalık sayıyı yeniden yerleştirir.

Adım adım:

embiggen()Fonksiyon atar onun argüman şeklinde tamsayı kesildi $intve nokta sonra sayıları kaydeder $fraction. Kesirli basamakların sayısı içinde not edilmiştir $precision. Matematik ait birleştirme ile çarpar 10¹⁰ $intve $fractionbundan sonra da (örneğin, bu hassas uyacak şekilde ayarlanır embiggen 48.8610¹⁰ x 4886/100 ve döner hale 488600000000488.600.000.000 olan).

Yüzde yüzlerce kesin bir hassasiyet istiyoruz, bu yüzden ilk sayıyı 100'le çarpıyoruz, yuvarlama amacıyla 5 ekliyoruz ve ardından ikinci sayıyı bölüyoruz. Bu ödev $answerbizi, son cevabın yüz katı olarak bırakır.

Şimdi ondalık basamağı eklememiz gerekiyor. Son iki basamağını hariç tutmak için yeni bir $intdeğer atarız $answer, daha sonra echobir nokta ve daha önce halledilmiş olan değer $answerhariç tutulur $int. (Bunu bir yorum gibi görünmesini sağlayan sözdizimi vurgulama hatasını boşver)

(Bashism: üstelleştirme POSIX değil, bu bir faşizmdir. Saf bir POSIX çözümü, on'un yetkilerini kullanmak yerine sıfır eklemek için döngüler gerektirir. Ayrıca, "embiggen" mükemmel bir renklendirici kelimedir.)


Kabuğum olarak kullanmamın en önemli sebeplerinden biri zshkayan nokta matematiğini desteklemesi. Bu sorunun çözümü oldukça basittir zsh:

printf %.2f $((float/1.18))

(Kayan nokta aritmetiğini etkinleştirmek için birisinin bu cevaba bir yorum eklediğini görmeyi çok isterdim bash, ancak böyle bir özelliğin henüz bulunmadığından eminim.)


1

sonucu aldıysanız, örneğin 2.3747888

tüm yapman gereken:

d=$(echo "(2.3747888+0.5)/1" | bc); echo $d

bu, sayıyı doğru bir şekilde yuvarlar:

(2.49999 + 0.5)/1 = 2.99999 

ondalık sayılar kaldırılır bcve bu sayede gerektiği gibi 2'ye yuvarlar.


1

Ses dosyalarının toplam süresini hesaplamam gerekiyordu.

Bu yüzden zorundaydım:

A. her bir dosya için süre gösterilmemiştir ( gösterilmemiştir )

B. bütün süreleri toplayın (her biri NNN.NNNNNN(fp) saniyede idi)

C. saatleri, dakikayı, saniyeyi, saniyeyi takip edin.

D. bir dizi HR: MIN: SEC: FRAMES yazdı, burada frame = 1/75 sn.

(Çerçeveler stüdyolarda kullanılan SMPTE kodundan gelir.)


A: kullanım ffprobesüresi ve ayrıştırma süresi satır bir fp numarası (gösterilmiyor)

B:

 # add them up as a series of strings separated by "+" and send it to bc

arr=( "${total[@]}" )  # copy array

# IFS is "Internal Field Separator"
# the * in arr[*] means "all of arr separated by IFS" 
# must have been made for this
IFS='+' sum=$(echo "scale=3; ${arr[*]} "| bc -l)# (-l= libmath for fp)
echo $sum 

C:

# subtract each amount of time from tt and store it    
tt=$sum   # tt is a running var (fp)


hrs=$(echo "$tt / 3600" | bc)

tt=$(echo "$tt - ( $hrs * 3600 )" | bc )

min=$(echo "$tt / 60" | bc )

tt=$(echo "$tt - ($min *60)" | bc )

sec=$(echo "$tt/1" | bc )

tt=$(echo "$tt - $sec" | bc )

frames=$(echo "$tt * 75"  | bc ) # 75 frames /sec 
frames=$(echo "$frames/1" | bc ) # truncate to whole #

D:

#convert to proper format with printf (bash builtin)        
hrs=$(printf "%02d\n" $hrs)  # format 1 -> 01 

min=$(printf "%02d\n" $min)

sec=$(printf "%02d\n" $sec)

frames=$(printf "%02d\n" $frames)

timecode="$hrs:$min:$sec:$frames"

# timecode "01:13:34:54"

1

İşte betiğinizin kısaltılmış bir versiyonu, istediğiniz çıktının sağlanması için düzeltildi:

#!/bin/bash
float=48.86
echo "You asked for $float; This is the price without taxes:"
echo "scale=3; price=$float/1.18 +.005; scale=2; price/1 " | bc

En yakın tamsayıya yuvarlamanın, 0 eklemeye ve zemini almaya veya aşağı yuvarlamaya (pozitif sayılar için) eşdeğer olduğunu unutmayın.

Ayrıca, ölçek faktörü operasyon sırasında uygulanır; yani (bunlar bckomuttur, bunları terminalinize yapıştırabilirsiniz):

float=48.86; rate=1.18; 
scale=2; p2=float/rate
scale=3; p3=float/rate
scale=4; p4=float/rate
print "Compare:  ",p2, " v ", p3, " v ", p4
Compare:  41.40 v 41.406 v 41.4067

# however, scale does not affect an entered value (nor addition)
scale=0
a=.005
9/10
0
9/10+a
.005

# let's try rounding
scale=2
p2+a
41.405
p3+a
41.411
(p2+a)/1
41.40
(p3+a)/1
41.41

1

İstenildiği gibi saf bc uygulaması

define ceil(x) { auto os,xx;x=-x;os=scale;scale=0 xx=x/1;if(xx>x).=xx-- scale=os;return(-xx) }

Bunu Fonksiyonlar.bc isimli bir dosyaya koyarsanız, o zaman tamamlayabilirsiniz.

echo 'ceil(3.1415)' | bc functions.bc

Http://phodd.net/gnu-bc/code/funcs.bc adresinde bulunan bc uygulamasının kodu


0
bc() {
        while :
        do
                while IFS='$\n' read i
                do
                        /usr/bin/bc <<< "scale=2; $i" | sed 's/^\./0&/'
                done
        done
}

0

Awk kullanabilirsiniz, boru gerekmez.

$> PRICE=48.86
$> awk -vprice=$PRICE "BEGIN{printf(\"%.2f\n\",price/1.18+0.005)}"
41.41

Fiyatı önceden ortam değişkeni ile ayarlayın PRICE=<val>.

  • O zaman awk'yi çağırın - $PRICEawk'ye awk değişkeni olarak gider price.

  • Daha sonra +.005 ile en yakın 100'e yuvarlanır.

  • Printf formatlama seçeneği %.2fölçeği iki ondalık basamakla sınırlar.

Bunu çok yapmak zorundaysanız, bu şekilde seçilen cevaptan çok daha verimli:

time for a in {1..1000}; do echo $(round 48.86/1.18 2) > /dev/random; done
real    0m8.945s
user    0m6.067s
sys 0m4.277s

time for a in {1..1000}; do awk -vprice=$PRICE "BEGIN{printf(\"%.2f\n\",price/1.18+0.005)}">/dev/random; done
real    0m2.628s
user    0m1.462s
sys 0m1.275s
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.