web-dev-qa-db-ja.com

「bc」を使用せずにシェルスクリプトで丸められたパーセンテージを計算する

シェルスクリプトで特定のアイテムの割合を計算しようとしています。値を四捨五入したいと思います。つまり、結果が59.5の場合、59ではなく60を期待する必要があります。

item=30
total=70
percent=$((100*$item/$total))

echo $percent

これにより42が得られます。

しかし、実際には、結果は42.8であり、43に切り上げます。「bc」はトリックを行います。「bc」を使用しない方法はありますか?

新しいパッケージをインストールする権限がありません。 「dc」と「bc」は私のシステムに存在しません。純粋にシェルである必要があり、Perlまたはpythonスクリプトも使用できません

15
user2354302

AWKを使用します(bash-ismなし):

item=30
total=70
percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }")

echo $percent
43
23
Michael Back

2 *元のパーセント計算を取得し、そのモジュロ2を取得すると、丸めの増分が得られます。

item=30
total=70
percent=$((200*$item/$total % 2 + 100*$item/$total))

echo $percent
43

(bash、ash、dash、kshでテスト済み)

これは、AWKコプロセスを起動するよりも高速な実装です。

$ pa() { for i in `seq 0 1000`; do pc=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }"); done; }
$ time pa

real    0m24.686s
user    0m0.376s
sys     0m22.828s

$ pb() { for i in `seq 0 1000`; do pc=$((200*$item/$total % 2 + 100*$item/$total)); done; }
$ time pb

real    0m0.035s
user    0m0.000s
sys     0m0.012s
8
Michael Back

POSIX準拠のシェルスクリプトは、シェル言語を使用した整数演算に制限されています( "符号付き長整数演算のみが必要です" )。 純粋なシェル解決策が必要エミュレート浮動小数点演算:

_item=30
total=70

percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
_
  • _100 * item / total_は、整数除算の結果を切り捨てパーセントで返します。
  • _1000 * item / total % 10 >= 5 ? 1 : 0_は小数点第1位を計算し、それが5以上の場合、整数の結果に1を加算して切り上げます。
  • no算術展開$((...))内で変数参照の前に_$_を付ける必要があることに注意してください。

-質問の前提に反して、外部ユーティリティの使用が許容される場合:


  • awksimpleソリューションを提供しますが、これには真の倍精度を使用するcaveatが付属していますバイナリ浮動小数点値であるため decimal表現での予期しない結果 -たとえば、_printf '%.0f\n' 28.5_ではなく_28_を試してください予想される_29_):
_awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
_
  • _-v_を使用してawkスクリプトの変数を定義する方法に注意してください。これにより、single-quotedliteralawkスクリプトと任意の値を明確に分離できます。 Shellから渡されます。

  • 対照的に、bcis POSIXユーティリティ (したがって、ほとんどのUnixライクなプラットフォームに存在することが期待される)およびarbitrary-precision算術演算、それは常に切り捨て結果であるため、丸めanotherユーティリティによって実行する必要があります。ただし、 原理的にはPOSIXユーティリティです であるにもかかわらず、printfは、浮動小数点形式指定子(上記のawk内で使用されるような)をサポートするために不要です。 動作する場合も動作しない場合もある(より簡単なawkソリューションを考えると、トラブルの価値はなく、浮動小数点演算による精度の問題が戻ってきた):
_# !! This MAY work on your platform, but is NOT POSIX-compliant:
# `-l` tells `bc` to set the precision to 20 decimal places, `printf '%.0f\n'`
# then performs the rounding to an integer.
item=20 total=70
printf '%.0f\n' "$(bc -l <<EOF
100 * $item / $total
EOF
)"
_
6
mklement0

以下は私の2番目の回答に基づいていますが、exprおよびback-ticks-(おそらく大部分は嫌いですが)最も古風なものでも動作するように適合させることができますネイティブのシェル:

item=30
total=70
percent=`expr 200 \* $item / $total % 2 + 100 \* $item / $total`

echo $percent
43
0
Michael Back