bash에서 bc를 사용하여 소수를 반올림하는 방법은 무엇입니까?


45

bash 스크립팅을 사용하여 원하는 것에 대한 간단한 예 :

#!/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

가격이 48.86이라고 가정하면 대답은 41.406779661입니다 (실제로 41.40을 사용하고 있기 때문에 scale=2;)

내 질문은 : 이 방법으로 답변을 표시하기 위해 두 번째 소수를 반올림하는 방법은 무엇입니까? : 41.41


"printf"% 0.2f \ n "41.445"는 작동하지만 "printf"% 0.2f \ n "41.435 및 printf"% 0.2f \ n "41.455"는 작동하기 때문에 이상합니다. 귀하의 사건도 (12.04에서) 작동하지만 .445와는 다릅니다.
Luis Alvarado

8
IMHO, 아무도이 질문에 만족스럽게 답변하지 못했습니다 . 아마도 bc요청 된 것을 얻지 못하기 때문에 (또는 적어도이 게시물을 찾았을 때 묻는 질문), (을 사용하여bc 호출되는 bash) 소수점 이하 자릿수를 만드는 방법 입니다.
Adam Katz

나는이 질문이 오래 되었다는 것을 알고 있지만 bc의 github.com/gavinhoward/bc 구현을 추진하는 의견에는 저항 할 수 없다 . 이를 위해 r()함수 와 함께 내장 라이브러리 에 있으므로 사용할 수 있습니다 bc -le "r($float/1.18, 2)".
개빈 하워드

답변:


31

bash round 함수 :

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

코드 예제에서 사용됩니다 :

#!/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..."

행운을 빕니다 : O)


1
btw, 만약 그 숫자가 음수라면, 우리는 다음을 사용해야합니다-0.5
Aquarius Power

테스트 콘솔에서 작동하는 예제를 얻을 수 없습니다! "디버깅 (Debugging)"은 예를 들어 $2 = 3내가 사용해야 한다는 것을 조금 밝혀냈다 echo $(env printf %.3f $(echo "scale=3;((1000*$1)+0.5)/1000" | bc)). printf 전에 env 를 염두에 두십시오 ! 이것은 다른 곳에서 복사하여 붙여 넣는 것을 이해 하는 것이 항상 중요하다는 것을 다시 가르쳐 줄 것 입니다.
syntaxerror

@Aquarius Power 영감을 주셔서 감사합니다! 이제 음수와 양수 모두에서 작동하는 위의 스크립트 버전을 포크했습니다.
syntaxerror

이것은 불필요하게 복잡하며 migas와 zuberuber가 제공하는 더 간단한 답변에 도달하기 위해 모든 종류의 추가 산술을 수행합니다.
Adam Katz

@AquariusPower +1 네, 음수는 다릅니다. 음수가 아닌 정수를 테스트하기 위해 비트를 시도하고 if [ echo "$ 1 / 1"| bc` -gt 0]`-보다 우아한 검사 방법이 있습니까 ( "-"에 대한 문자열 구문 분석 제외)?
RobertG

30

가장 간단한 해결책 :

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

2
위의 의견 ( askubuntu.com/a/179949/512213 ) 에서 언급했듯이 불행히도 음수에 대해서는 잘못 작동합니다.
RobertG

2
@RobertG : 왜? 이 솔루션은을 사용하지 않습니다 +0.5. 시도 printf "%.2f\n" "$(bc -l <<<"48.86/1.18")" "$(bc -l <<<"-48.86/1.18")"하고 당신은 얻을 것이다 41.41-41.41.
musiphil

1
파이프를 사용하여도 가능 :echo "$float/1.18" | bc -l | xargs printf %.2f
디저트

이 답변의 솔루션은 bash: printf: 1.69491525423728813559: invalid number로케일에 따라 다음을 제공합니다 .
Torsten Bronger

19

배쉬 / awk 반올림 :

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

파이썬이 있다면 다음과 같은 것을 사용할 수 있습니다 :

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

팁 주셔서 감사하지만 필요한 것을 해결하지 못합니다. 난 그냥 bash를 사용하여 그것을해야합니다 ...
blackedx

1
python 명령은 더 읽기 쉽고 빠른 스크립트에 적합합니다. round 함수에 ", 3"을 추가하여 임의의 숫자 반올림도 지원합니다. 감사합니다
Elle

echo "$float" |awk '{printf "%.2f", $1/1.18}'질문의 요청 된 수학을 요청 된 백분의 일에 수행합니다. 그것은 bc질문에 대한 호출 만큼 "bash 사용" 입니다.
Adam Katz

2
파이썬의 python -c "print(round($num))"경우 where 와 같은 것을 사용할 수 있습니다 num=4.678923. stdin을 사용하지 않아도됩니다. 다음과 같이 n 자리로 반올림 할 수도 있습니다 python -c "print(round($num, $n))".
Six

나는 그 해결책으로 꽤 깔끔한 것을 할 수 있었고, 내 문제는 0.5였다 echo 5 | python -c "print int(round((float(raw_input())/2)-0.5))".
Yaron

4

순전히 bc 솔루션이 있습니다. 반올림 규칙 : +/- 0.5에서 0에서 반올림합니다.

찾고있는 스케일을 $ result_scale에 넣으십시오. bc 명령 목록에서 $ MATH 위치에 수학이 있어야합니다.

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
매우 흥미로운! MATH=-0.34;result_scale=1;bc <<MATH, #ObjectiveAndClear : 5 여기에 설명 된대로 :)
Aquarius Power

"최고의 답변"으로 추천합니다.
마크 탐 스키

2

나는 그것이 오래된 질문이라는 것을 알고 있지만 'if'나 가지가없는 순수한 'bc'- 솔루션이 있습니다.

#!/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' 5또는 bcr '0.666666' 2-> 와 같이 사용하십시오 (표현식과 스케일)

C / C ++와 같은 bc에서는 계산에 논리 표현식을 혼합 할 수 있기 때문에 가능합니다. 표현식 ((t>0)-(t<0))/2)은 't'의 부호에 따라 +/- 0.5로 평가되므로 반올림에 올바른 값을 사용합니다.


깔끔하게 보이지만 대신에 bcr "5 / 2" 0반환 2됩니다 3. 뭔가 빠졌습니까?
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..."

실제로 간단합니다. 음수에 대해 하드 코딩 된 "-0.5"변형을 명시 적으로 추가 할 필요가 없습니다. 수학적으로 말, 우리는 단지 계산거야 절대 값 인수를 여전히 추가 우리가 평소대로 0.5. 그러나 (불행히도) abs()우리가 처리 할 때 내장 함수 가 없기 때문에 ( 코드를 작성하지 않는 한) 단순히 부정 할 것입니다 인수가 부정적이라면 입니다.

또한 을 매개 변수로 사용하는 것은 매우 번거로운 것으로 판명되었습니다 (내 솔루션 때문에 배당 자와 제수에 별도로 액세스 할 수 있어야합니다). 이것이 내 스크립트에 추가 세 번째 매개 변수가있는 이유입니다.


최근 편집에 대한 @muru : 쉘 산술 대신 herestrings 대신 echo .. |요점은 echo $()무엇입니까? 이것은 설명하기 쉽습니다 : here 문자열은 항상 쓰기 가능한/tmp 디렉토리가 필요합니다 ! 그리고 내 솔루션은, 예를 들어 비상 루트 쉘 읽기 전용 환경에서 작동 /입니다 하지 항상 기본적으로 쓰기. 그래서 내가 그렇게 코딩 한 이유는 충분했습니다.
syntaxerror

여기에 동의합니다. 그러나 언제 echo $()필요한가? 그 들여 쓰기로 편집 내용이 나타났습니다.
muru

@muru 글쎄요, 가장 무의미한 사건은 고쳐서 기한이 지났는데 echo $()사실 두 번째 줄인 lol이었습니다. 헤드 업 주셔서 감사합니다. 적어도 지금은 훨씬 나아 보이지만 거부 할 수 없습니다. 어쨌든, 나는 이것의 논리를 좋아했습니다. 많은 부울 항목, 덜 불필요한 "if"(확실히 코드를 50 % 증가시킵니다).
syntaxerror

1

bc함수 내에서 하나의 값만 반올림하는 방법 에 대한 순수한 대답을 찾고 있지만 순수한 bash대답은 다음과 같습니다.

#!/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..."

기본적으로 이것은 소수점을 조심스럽게 추출하고 모든 것을 1,000 억 (10¹ 10**10in bash)으로 곱하고 정밀도와 반올림을 조정하고 실제 나누기를 수행하고 적절한 크기로 다시 나누고 소수점을 다시 삽입합니다.

단계별 :

embiggen()함수는 잘린 정수 형식의 인수를 할당하고 $int에 점 다음에 숫자를 저장합니다 $fraction. 소수 자릿수는에 표시되어 $precision있습니다. 수학의 연결에 의해 곱 10¹⁰ $int$fraction후와 그 예는 (정밀도에 맞게 조정 embiggen 48.8610¹⁰ × 100분의 4,886 반환된다488600000000 488,600,000,000)입니다.

우리는 100 분의 1의 정밀도를 원하므로 첫 번째 숫자에 100을 곱하고 반올림 목적으로 5를 더한 다음 두 번째 숫자를 나눕니다. 이 임무는 $answer우리에게 최종 답변의 백 배 를 남깁니다.

이제 소수점을 추가해야합니다. 우리는 새로운 지정 $int에 가치를 $answer다음의 마지막 두 자리를 제외 echo점으로 그것을하고 $answer제외$int 이미 알아서 값을. (이것이 주석처럼 보이게하는 구문 강조 버그는 신경 쓰지 마십시오)

(Bashism : 지수화는 POSIX가 아니므로 이것은 bashism입니다. 순수한 POSIX 솔루션은 10의 거듭 제곱을 사용하는 대신 0을 추가하기 위해 루프를 필요로합니다. 또한 "embiggen"은 완벽하게 cromulant 단어입니다.)


zsh쉘로 사용하는 주된 이유 중 하나 는 부동 소수점 수학을 지원하기 때문입니다. 이 질문에 대한 해결책은 다음과 같이 매우 간단합니다 zsh.

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

(누군가에서 부동 소수점 산술을 활성화하는 트릭 으로이 답변에 의견을 추가하고 싶지만 bash그러한 기능이 아직 존재하지 않는다는 것을 확신합니다.)


1

예를 들어 결과가있는 경우 2.3747888을 고려하십시오.

당신이해야 할 일은 :

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

이것은 숫자를 올바르게 반올림합니다.

(2.49999 + 0.5)/1 = 2.99999 

소수점 이하 자릿수는 제거 bc되므로 2로 내림합니다.


1

오디오 파일 모음의 총 지속 시간을 계산해야했습니다.

그래서 나는해야했다 :

A. 각 파일의 지속 시간을 얻습니다 (표시 되지 않음 ).

B. 모든 지속 시간을 합산하십시오 (각각 NNN.NNNNNN(fp) 초 였습니다 )

C. 별도의 시간, 분, 초, 서브 초.

D. HR : MIN : SEC : FRAMES 문자열을 출력합니다. 여기서 frame = 1/75 sec.

(프레임은 스튜디오에서 사용되는 SMPTE 코드에서 가져옵니다.)


A : ffprobe지속 시간 줄을 사용하여 fp 번호로 구문 분석하십시오 (표시되지 않음)

비:

 # 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 

씨:

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

디:

#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

다음은 원하는 출력을 제공하도록 수정 된 약식 스크립트 버전입니다.

#!/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

가장 가까운 정수로 반올림하는 것은 0.5를 더하고 바닥을 차지하거나 반올림 (양수)하는 것과 같습니다.

또한 스케일 팩터는 작동시 적용됩니다. 따라서 (이러한 bc명령 은 터미널에 붙여 넣을 수 있습니다) :

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


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

0

파이프가 필요없는 awk를 사용할 수 있습니다.

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

환경 변수를 사용하여 가격을 미리 설정하십시오 PRICE=<val>.

  • 그런 다음 awk-를 호출하여 $PRICEawk 변수로 awk로 이동합니다 price.

  • 그런 다음 +.005를 사용하여 가장 가까운 100으로 올림합니다.

  • printf 형식 옵션 %.2f은 스케일을 소수점 이하 두 자리로 제한합니다.

이것을 많이해야한다면이 방법은 선택한 답변보다 훨씬 효율적입니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.