4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VBの IsNumeric の限界値から正体を探る

Posted at

VBの IsNumeric の限界値から正体を探る

結論

Visual BasicのIsNumericTrueと判定する最大/最小の整数値↓(309桁)

digits: 309
upper integer limit: 17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
digits: 309
lower integer limit: -17976931348623158099999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

Double型との共通点がみられた。

IsNumericTrueだったからって、そのまま数値型に変換してはいけない。

経緯

仕事で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の人も気を付けよう。

おわり

気づきまではいったが、知識不足でそこからの考察が片手落ちになってしまった。
時間があれば検証コードを書き直して、小数も含めて限界値を測ってみたい。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?