Bashのarithmetic expansionと(( ))は基本的にinteger計算です。divisionの小数部、leading zeroのoctal解釈、外部inputの式解釈、overflowを理解せず金額やmeasurementへ使うと誤差が出ます。小数や高精度はbc、awk、application language等へ切り替えます。
結論は「信頼済みintegerは$(( expression ))、状態更新とstatus判定は(( expression ))を使い、外部inputはdigitsとrangeを先に検証します。小数・丸め規則が必要ならbc等へ明示的に渡します」です。
Bash整数計算の範囲を決める
arithmetic expansionは結果をwordとして返し、arithmetic command (( expression ))は式の値が0ならstatus 1、0以外ならstatus 0です。数字のleading 0はoctal、0xはhex等のbase表記として解釈されます。Bashはfloating pointを直接supportしません。
- integer・decimal・currency・durationのどの型か
- inputの許可文字、最小/最大、empty/negative
- division by zeroとremainderの扱い
- leading zeroとbaseの意味
- 丸め方法・overflow・Bash word size
計算前のraw inputとvalidation結果、式、期待単位を記録します。user inputをそのままarithmetic expansionへ入れると、variable参照やoperatorとして解釈される可能性があります。caseまたはregular expressionでdecimal digits等のallowlistを通します。
計算対象がcounter、byte、秒、金額、割合、統計のどれかを決め、integerで表せるか、必要桁、負数、最大値、丸め規則を定義します。Bashのarithmeticは整数で、10/3は3となり小数を保持しません。外部入力は空、符号、leading zero、非数字、巨大値を検証し、式として直接評価しません。08のような値がoctal解釈でerrorになる点も確認します。
入力を検証してから式へ入れる
整数は単位を最小unitへそろえ、price_centsやduration_msのようにvariable名へ単位を含めます。inputを検証し、leading zeroをdecimalとして扱う場合は10#を使います。小数はscaleと丸めを指定できるbc等で計算し、locale decimal separatorも固定します。
整数の合計
price_cents=1250
quantity=3
total_cents=$((price_cents * quantity))
printf 'total_cents=%s\n' "$total_cents"
moneyは小数floatでなく最小通貨unitのintegerとして扱う方法があります。
counterを更新
count=0
((count += 1))
printf 'count=%s\n' "$count"
(( ))のstatusは式の値に依存するためset -e環境でcount++の使い方に注意します。
divisionとremainder
total=10
parts=3
quotient=$((total / parts))
remainder=$((total % parts))
printf 'q=%s r=%s\n' "$quotient" "$remainder"
integer divisionなので3.333ではなくquotient 3、remainder 1です。
decimal digitsを検証
raw=08
case $raw in ''|*[!0-9]*) printf '%s\n' 'invalid integer' >&2; exit 2;; esac
value=$((10#$raw))
validation後に10#でdecimalとして扱い、emptyやoperatorを拒否します。
bcで小数計算
printf '%s\n' 'scale=4; 10 / 3' | bc
scaleとrounding要件を確認し、untrusted expressionをそのままbcへ渡しません。
信頼済み整数は$((expression))で値を得て、状態判定やincrementは((expression))のexit statusに注意して使います。decimal文字列を整数へ変える場合はdigitsをallowlist検証してから10#を使います。金額はcents等の最小単位整数、少数計算はbcやawkへ移し、scaleと丸めを明示します。division前にdenominatorが0でないことを確認します。
base・division・exit statusを含む計算例を理解する
$(( ))はshell wordへinteger結果を展開します。(( ))はassignment、comparison、increment等を記述でき、結果0がfalse statusです。前置incrementと後置incrementで式の値が違うためset -eと組み合わせると意図せずexitする場合があります。bcのscaleも金融rounding規則を自動で満たすとは限りません。
- Bash arithmeticはfixed-width integerでfloating pointではない
- integer divisionは小数部を保持しない
- leading 0の数字はoctalとして解釈され得る
- (( expression ))は式が0ならnonzero statusを返す
- 小数はbc等でscaleとroundingを明示する必要がある
arithmetic expansionは整数結果をwordとして返し、arithmetic commandは式の値が0ならstatus 1、0以外ならstatus 0を返します。そのためset -e環境で((count++))がcount=0から始まると予期せず終了することがあります。Bash整数のbit幅とoverflowは環境依存で、金融・科学計算の精度保証には不十分です。bcのscaleも自動で業務の丸め規則を満たすわけではありません。
金額と外部inputを安全に扱う
user inputをarithmetic codeとして評価しません。金額、billing、tax、scientific dataはdomainのrounding ruleとoverflow testを設け、shellだけで完結させる適性をreviewします。division by zero、negative、巨大値で後続file countやsleep時間が危険にならない上限を設定します。
- Bashで10/3が3.333になると思う
- 08をdecimal 8として無条件に計算する
- ((count++))のstatusを無視する
- untrusted文字列を式へ直接入れる
- bc scaleを正式な会計roundingと同一視する
ユーザー入力を$((input))へそのまま入れると、変数参照や演算子として評価され得ます。digits、範囲、単位を検証し、timeoutやloop回数に使う値には上限を設けます。請求額、税、rateは会計規則のrounding modeと時点を確認し、binary floatingや表示丸めだけで決めません。division by zeroとoverflowで後続削除・請求処理が誤動作しないようfailure時は停止します。
境界値と丸めをtestする
0、1、negative、leading zero、最大値付近、division by zero、empty、非数字、小数をtestします。別のcalculatorまたはunit testで期待値を照合し、単位、rounding、status、stderrを記録します。
- data型・単位・range・rounding ruleを定義した
- 外部inputをallowlist validationした
- integerとdecimalのtoolを使い分けた
- 境界値とdivision by zeroをtestした
0、1、負数、最大付近、leading zero、空、非数字、division by zero、端数が出る値をtestします。別の信頼できるcalculatorまたはunit testで期待値を照合し、単位、途中値、丸め前後、exit statusを記録します。set -eあり・なしでcounterのincrementが同じflowになるか、localeが小数点の入力へ影響しないかも確認します。
Bashと専用計算toolを使い分ける
counter、file size、秒数等の単純integerはBashで扱えます。decimal money、statistical calculation、large dataset、複雑なformulaはbc、awk、Python等へ移し、test可能なfunctionとschemaを用意します。
単純なcountや整数sizeはBash、decimal moneyや複雑なformulaはbcまたは型とdecimal libraryを持つ言語、集計データはawk・Python・SQLを選びます。shellで計算できることと保守しやすいことは別です。式が長い、unitが混在、丸めが監査対象、入力規模が大きい場合は、関数化とtestが容易な専用処理へ移します。

コメント