はじめに
これは、Visual Basic Advent Calendar 2021の6日目の記事となります。
先月ネタ探しに、Yahooリアルタイム検索でVisual Basic関連で検索していた時に、下記のサイトを見つけました。
現象
If ((3091470 - 714643) * 0.2 * 62) mod 365 = 0 Then
Call MsgBox("割り切れる")
Else
Call MsgBox("割り切れない")
End If
上記のプログラム “sample-mod-bug1.vbs” を Windows PC に保存して実行すると「割り切れる」とメッセージが表示されます。
実際に… ((3091470 – 714643) * 0.2 * 62) ÷ 365 を計算すると、答えは 807,469.995 になります。従って、剰余は 0 ではないので「割り切れない」と表示されるべきです、、
原因
これは不具合ではなく、Classic Visual Basic系(VBScript、VBA、Visual Basic 6.0)では整数に対する剰余しか対応していないという仕様でした。その為、VB.NETではこの不具合は発生しません。
解説
剰余演算子は、number1 を number2 で割って (浮動小数点数は整数に四捨五入されます)、その余りのみを result として返します。
A = 19 Mod 6.7 ' Returns 5.
MyResult = 12.6 Mod 5 ' Returns 3.
https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/mod-operator
https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa242823(v=vs.60)?redirectedfrom=MSDN
((3091470 – 714643) * 0.2 * 62) = 29472654.8 は整数に四捨五入され 29472655 となります。
29472655だと365で割り切れるため剰余も0となり、「割り切れる」というメッセージとなります。
29472655 Mod 365 = 0
29472655 / 365 = 807470
余談
ちなみにC言語では整数に対する剰余は%
演算子で求められ、浮動小数点を指定するとコンパイルエラーになります。
浮動小数点数に対してはfmod
関数を使用する必要があります。
.NET系では、Visual BasicはMod
演算子(C#は %
演算子)では、浮動小数点数の剰余に対応しています。
サポートされている型
すべての数値型。 これには、符号なしおよび浮動小数点の型と Decimal が含まれます。
https://docs.microsoft.com/ja-jp/dotnet/visual-basic/language-reference/operators/mod-operator
対応
Mod 演算子は使用しないでfmod
関数を作成する。
If fmod((3091470 - 714643) * 0.2 * 62, 365) = 0 Then
Call MsgBox("割り切れる")
Else
Call MsgBox("割り切れない")
End If
Function fmod(a,b)
fmod = a - b * Int(a / b)
End Function
VBAのMod演算子とExcelのサポートしているMod数式の計算結果は異なる。
参照:剰余の計算でMod演算子とMod数式の結果が違う - Excel VBAコーディング ガイドライン案
最後に
以前、端数処理でExcelで計算した結果と違うので不具合ではないかとユーザーから確認があったことを思い出しました。ユーザーはExcelを使用していることが多いので、Excelの数式とプログラム言語での結果が違うというのは困り物ですね。