VBの IsNumeric の限界値から正体を探る
結論
Visual BasicのIsNumeric
がTrue
と判定する最大/最小の整数値↓(309桁)
digits: 309
upper integer limit: 17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
digits: 309
lower integer limit: -17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
Double
型との共通点がみられた。
IsNumeric
がTrue
だったからって、そのまま数値型に変換してはいけない。
経緯
仕事でVB6からC#への書き換え中、テキストボックスへの入力値をIsNumeric
のみで判定しSingle型に変換するコードを発見した。※
これ上限/下限判定どうなっとんじゃい、ということでググったが、どこにもIsNumeric
の限界が書いてなかった。
せっかくなので検証コードを書き、IsNumeric
の限界を調べてみた。
※当然、実質的にはSingle
型の範囲がそのまま入力値を制限する。でも判定用メソッドでちゃんと弾いてほしいよね。
検証
2024/10/04現在、VB6の開発環境を構築することは難しい。
しかし、.Net FrameworkのMicrosoft.VisualBasic
名前空間にVBの互換メソッドが詰まっている。※
私自身がC#に慣れているのもあるので、今回はこちらをつかって検証した。
フレームワークは.NET Framework 4.6.1
。
※プロジェクトにMicrosoft.VisualBasic
アセンブリへの参照を追加しないと使えないので注意。
以下、検証用コード。
using System;
using Microsoft.VisualBasic;
public class Program
{
public static void Main()
{
Console.WriteLine("upper integer limit: " + IntegerLimit(true));
Console.WriteLine("lower integer limit: " + IntegerLimit(false));
Console.ReadLine();
}
private static string IntegerLimit(bool isPositive)
{
string confirmed = isPositive ? "1" : "-1"; // 確定した位
int changing = 0; // いじる位
int zeros = 0; // ゼロの桁数
// 何桁までいけるのか調べる
while (true)
{
string text = confirmed + changing.ToString() + new string('0', zeros);
bool isNumeric = Information.IsNumeric(text);
if (isNumeric)
{
zeros++;
}
else
{
Console.WriteLine("digits: " + (zeros + 1));
zeros--;
break;
}
}
// 具体的な数字を上の位から順に調べる
while (zeros > 0)
{
string text = confirmed + changing.ToString() + new string('0', zeros);
bool isNumeric = Information.IsNumeric(text);
if (isNumeric)
{
changing++;
if (changing > 9)
{
Confirm();
}
}
else
{
Confirm();
}
// 一桁確定させる内部メソッド
void Confirm()
{
changing--;
confirmed = confirmed + changing.ToString();
zeros--;
changing = 0;
}
}
return confirmed;
}
}
結果:
digits: 309
upper integer limit: 17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
digits: 309
lower integer limit: -17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
考察
まず、正負どちらも数値部分の桁数は変わらず309桁だった。
また、左から数えて比較的早い段階(18桁)で数値は変化しなくなり、あとは9が埋めることになった。
ところで、VBのDouble
型の範囲はこんな感じだ。
-1.79769313486231570 E + 308 から -4.94065645841246544 E-324 (負の値の場合) および 4.94065645841246544 E-324 から 1.79769313486231570 E + 308 (正の値の場合) までの値の範囲の、符号付き IEEE 64 ビット (8 バイト) の倍精度浮動小数点数を保持します。 倍精度の数値は、実数の概数を格納します。
1797693134862315
まで、IsNumeric
の限界値と一致している。おまけに、桁数も一致している(調査結果の方が1多いのは、Double
の方では数えられていない一番左の1も数えているから)
このことから、IsNumericでは内部ではDouble
型がかかわる形で変換しているのでは?と推測できる。
しかし1797693134862315
以降はずれが生じ、IsNumeric
の方が絶対値が若干大きい範囲まで数値と判定しているのが謎。
そもそも、IsNumeric
はかなり対応の幅が広い(悪い意味で)。
そのままでは数値型にキャストできない文字列もTrue
を返してしまうそうだ。
いずれにせよ上記のURLにもあるように、これだけを使って判定するのはよした方がよさそう。
今VBで開発してる人いる? VBAの人も気を付けよう。
おわり
気づきまではいったが、知識不足でそこからの考察が片手落ちになってしまった。
時間があれば検証コードを書き直して、小数も含めて限界値を測ってみたい。