VBAで正の無限大・負の無限大を取得する

VBAで正の無限大・負の無限大を取得する方法のメモです。

# はじめに

VBAの組み込み型の一つの[Double データ型](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/double-data-type "Double データ型 | Microsoft Docs")は、通常のVBAの操作の範囲内では、有限の小数しか扱うことができません。

しかし、Double型はVBA独自の表現では無く、他のプログラミング言語で使われている[倍精度浮動小数点数](https://ja.wikipedia.org/wiki/%E5%80%8D%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0 "倍精度浮動小数点数 - Wikipedia")と同じ表現のため、正の無限大や負の無限大、非数などを値として持つことができます。

この記事ではDouble型に正の無限大や負の無限大、非数を設定する方法を記載します。

## 参考

[VBAのDouble型](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/double-data-type "Double データ型 | Microsoft Docs")のページには

> IEEE 64 ビット (8 バイト) 浮動小数点数

と記載があり、

[この資料](http://interoperability.blob.core.windows.net/files/MS-VBAL/[MS-VBAL].pdf "")の15ページには

> All valid IEEE 754-1985 double-precision binary
floating-point numbers including sized zeros, NaNs and
infinities 

とあり、[IEEE 754-1985](https://ja.wikipedia.org/wiki/IEEE_754 "IEEE 754 - Wikipedia")に準拠しているとなっています。

# コード

実際に正の無限大・負の無限・非数を取得するコードです。

以下のコードの`PositiveInfinity`・`NegativeInfinity`・`NaN`プロシージャの値が、それぞれ正の無限大・負の無限・非数となります。

```vb
'モジュール宣言部
    'LSetでDouble型の中身を書き換えるため、
    'Double型および8バイト分(=Double型の大きさ)Byte配列をもつユーザー定義型を用意する・
Private Type udtByte8
    Value(0 To 7) As Byte
End Type

Private Type udtDouble
    Value As Double
End Type

'正の無限大を取得する。
Public Property Get PositiveInfinity() As Double
    Dim uB8 As udtByte8
    Dim uDb As udtDouble
    '念のため大きさチェック。
    Debug.Assert (Len(uB8) = 8) And (Len(uDb) = 8)
    
    With uB8
        .Value(UBound(.Value) - 1) = 240
        .Value(UBound(.Value)) = 127
    End With
    
    LSet uDb = uB8
    
    Let PositiveInfinity = uDb.Value
End Property

'負の無限大を取得する。
Public Property Get NegativeInfinity() As Double
    Dim uB8 As udtByte8
    Dim uDb As udtDouble
    '念のため大きさチェック。
    Debug.Assert (Len(uB8) = 8) And (Len(uDb) = 8)
    
    With uB8
        .Value(UBound(.Value) - 1) = 240
        .Value(UBound(.Value)) = 255
    End With
    
    LSet uDb = uB8
    
    Let NegativeInfinity = uDb.Value
End Property


'非数(Not a Number)を取得する。
Public Property Get NaN() As Double
    Dim uB8 As udtByte8
    Dim uDb As udtDouble
    '念のため大きさチェック。
    Debug.Assert (Len(uB8) = 8) And (Len(uDb) = 8)
    
    With uB8
        .Value(UBound(.Value) - 1) = 248
        .Value(UBound(.Value)) = 255
    End With
    
    LSet uDb = uB8
    
    Let NaN = uDb.Value
End Property
```

# コードの解説

「正の無限大」などは、Double型の中身が特定のパターンとなったときを示しています。

そのため、そのパターンとなるようにDouble型の中身を書き換えれば「正の無限大」などが得られることになります。

## 書き換える方法

上記のコードでは、[LSet ステートメント](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/lset-statement "LSet ステートメント (VBA) | Microsoft Docs")を使って中身を書き換えています。

ユーザー定義型を対象にLSetを使用すると、中の型をスルーしてバイナリでデータのコピーが行われます。

上記のコードではDouble型のみを要素として持つ`udtDouble`に、Double型と同じ大きさのByte型配列を持つ`udtByte8`をコピーすることで、中身を書き換えています。

## (参考)「特定のパターン」の調べ方

[倍精度浮動小数点数](https://ja.wikipedia.org/wiki/%E5%80%8D%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0 "倍精度浮動小数点数 - Wikipedia")内にも記載があったりと、ネットで調べればすぐに出てきますが、
PowerShellなどを使って実際に調べることもできます。

例えば以下のコードは、正の無限大・負の無限大・非数について、バイト毎に分解した情報をコンソールに表示します。

```powershell
@(
    'PositiveInfinity'
    'NegativeInfinity'
    'NaN'
) | ForEach-Object -Process {
    $_
    [System.BitConverter]::GetBytes([double]::$_) -join ","
}
```

```:結果
PositiveInfinity
0,0,0,0,0,0,240,127
NegativeInfinity
0,0,0,0,0,0,240,255
NaN
0,0,0,0,0,0,248,255
```

# 他の方法

Win32 APIなどで、Double型の中身を直接書き換えても、可能なはずです(未検証)。

また、JScriptであれば`Number.POSITIVE_INFINITY`などで取得できるため、JScript経由で取得する方法もあります。

[VBAからJScriptのfunctionオブジェクトを使用する（64bit対応） - Qiita](https://qiita.com/nukie_53/items/297e524bcc8e43f9b5d1 "VBAからJScriptのfunctionオブジェクトを使用する（64bit対応） - Qiita")の`JSFunc`を使用した例

```vb
Public Property Get PositiveInfinity()
    Let PositiveInfinity = JSFunc("", "Number.POSITIVE_INFINITY")(Empty)
End Property
```


# 参考ページ

[Double データ型 | Microsoft Docs (VBAのDouble型)](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/double-data-type "Double データ型 | Microsoft Docs")
[[MS-VBAL].pdf](http://interoperability.blob.core.windows.net/files/MS-VBAL/[MS-VBAL].pdf "")
[Double Struct (System) | Microsoft Docs (.NETのDouble型)](https://docs.microsoft.com/ja-jp/dotnet/api/system.double?view=netframework-4.8 "Double Struct (System) | Microsoft Docs")
[倍精度浮動小数点数 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%80%8D%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0 "倍精度浮動小数点数 - Wikipedia")
[IEEE 754 - Wikipedia](https://ja.wikipedia.org/wiki/IEEE_754 "IEEE 754 - Wikipedia")
[LSet ステートメント (VBA) | Microsoft Docs](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/lset-statement "LSet ステートメント (VBA) | Microsoft Docs")