業務システム開発・保守の実務メモを発信中

VBAで文字列を数値に変換するときのVal、CInt、CDbl、IsNumericの違い

Excel VBAでセルの値や入力値を扱っていると、文字列の "123""123.5" を数値として使いたい場面があります。

そこで調べ始めると、ValCIntCDblIsNumeric が出てきます。どれも数値まわりで使う関数なので、最初は「結局どれを使えばいいのか」というのが分かりにくいところです。

迷いやすい原因は、変換する関数と判定する関数、文字列の読み方が違う関数が同じ候補として並んでいることです。"123" ならどれを使っても似た結果に見えますが、"123.5""123abc" になると差が出ます。

代表的な3つの文字列で動きを比べると、どこで関数を選び間違えやすいかが見えてきます。変換処理では「判定したいのか」「整数にしたいのか」「小数を残したいのか」「先頭の数値だけ拾いたいのか」を分けて考えるのが出発点になります。

目次

4つの関数の役割

4つの関数は、同じ種類として見ないほうが分かりやすいです。

CIntCDbl は、文字列や式を指定した数値型へ変換するための関数です。CIntIntegerCDblDouble に変換します。

IsNumeric は、式全体を数値として評価できるかを True / False で返します。数値に変換した値を返す関数ではありません。

Val は少し性格が違っていて、文字列を先頭側から読み、数値の一部として認識できない文字に到達すると読み取りを停止します。たとえば "123abc" のような文字列でも、先頭の 123 だけを数値として取り出せます。

ただし、Val が小数点として認識するのはピリオド . です。ロケールを考慮して数値へ変換したい場面では、Val より型変換関数のほうが合っています。

動作を比べるコード

ということで、今回も動作を確認するためのコードを作りました。

次のコードでは、"123""123.5""123abc" をそれぞれ処理し、戻り値とVBA上の型をImmediateウィンドウへ出力します。

見る場所は、値そのものと角かっこ内の型です。123 と表示されていても、IntegerDouble では後続の計算や代入で意味が変わります。IsNumeric だけは変換結果ではなく、判定結果として True / False を出します。

VB
Option Explicit

Public Sub PrintNumberConversionResults()
    PrintValueResults "123"
    PrintDecimalResults "123.5"
    PrintTextSuffixResults "123abc"
End Sub

Private Sub PrintValueResults(ByVal sourceText As String)
    ' 同じ文字列を各関数へ渡し、戻り値だけでなくVBA上の型も比べる。
    Debug.Print Quote(sourceText) & " / Val       => " & Val(sourceText) & " [" & TypeName(Val(sourceText)) & "]"
    Debug.Print Quote(sourceText) & " / CInt      => " & CInt(sourceText) & " [" & TypeName(CInt(sourceText)) & "]"
    Debug.Print Quote(sourceText) & " / CDbl      => " & CDbl(sourceText) & " [" & TypeName(CDbl(sourceText)) & "]"
    Debug.Print Quote(sourceText) & " / IsNumeric => " & IsNumeric(sourceText) & " [Boolean]"
End Sub

Private Sub PrintDecimalResults(ByVal sourceText As String)
    ' 小数を整数化したいのか、小数のまま扱いたいのかで選ぶ関数が変わる。
    Debug.Print Quote(sourceText) & " / CInt      => " & CInt(sourceText) & " [" & TypeName(CInt(sourceText)) & "]"
    Debug.Print Quote(sourceText) & " / CDbl      => " & CDbl(sourceText) & " [" & TypeName(CDbl(sourceText)) & "]"
End Sub

Private Sub PrintTextSuffixResults(ByVal sourceText As String)
    ' Valは先頭から読める数値部分だけを読み、型変換関数は文字列全体を変換対象にする。
    Debug.Print Quote(sourceText) & " / Val       => " & Val(sourceText) & " [" & TypeName(Val(sourceText)) & "]"
    Debug.Print Quote(sourceText) & " / IsNumeric => " & IsNumeric(sourceText) & " [Boolean]"
    Debug.Print Quote(sourceText) & " / CInt      => " & ConversionResult("CInt", sourceText)
    Debug.Print Quote(sourceText) & " / CDbl      => " & ConversionResult("CDbl", sourceText)
End Sub

Private Function ConversionResult(ByVal functionName As String, ByVal sourceText As String) As String
    On Error GoTo ConversionError

    Select Case functionName
        Case "CInt"
            ConversionResult = CStr(CInt(sourceText)) & " [" & TypeName(CInt(sourceText)) & "]"
        Case "CDbl"
            ConversionResult = CStr(CDbl(sourceText)) & " [" & TypeName(CDbl(sourceText)) & "]"
    End Select
    Exit Function

ConversionError:
    ConversionResult = "Error " & Err.Number & ": " & Err.Description
    Err.Clear
End Function

Private Function Quote(ByVal value As String) As String
    Quote = """" & value & """"
End Function

"123abc"CIntCDbl にそのまま渡すとエラーになるため、ここでは ConversionResult でエラー番号とメッセージを文字列にしています。失敗する可能性のある変換をそのまま書くと処理が止まるので、この違いは見ておいたほうが安全です。

このコードは、関数の優劣を決めるためではなく、同じ文字列でも、関数ごとに「どこまで読むか」「どの型にするか」「判定だけで終わるか」が違うことを見るためのものです。

整数文字列の変換

"123" のように文字列全体が素直な数値なら、どの関数でも大きな違和感は出ません。

Markdown
"123" / Val       => 123 [Double]
"123" / CInt      => 123 [Integer]
"123" / CDbl      => 123 [Double]
"123" / IsNumeric => True [Boolean]

値だけ見ると 123 で揃っています。ただし型は違います。

CIntInteger を返します。整数として扱いたいときの変換です。CDblDouble を返すので、小数を含む計算まで見込むならこちらを選びます。

Val もこの例では 123 を返しますが、結果の型は Double でした。IsNumericTrue を返しているだけで、123 そのものへ変換しているわけではありません。

小数文字列とCInt、CDbl

"123.5" を扱うと、CIntCDbl の違いがはっきり出ます。

Markdown
"123.5" / CInt      => 124 [Integer]
"123.5" / CDbl      => 123.5 [Double]

CInt("123.5")124 になりました。CInt は小数部を単純に切り捨てる関数ではありません。Microsoft Learnでも、CInt は小数部分を丸め、ちょうど .5 の場合は最も近い偶数へ丸めると説明されています。

一方、CDbl("123.5")123.5 のまま Double として扱えます。

金額、数量、割合などで小数を残したいなら、CInt に入れた時点で情報が落ちます。整数へ丸めたい理由があるなら CInt、小数として計算したいなら CDbl と考えると迷いにくいです。

文字を含む文字列

"123abc" のように、数字の後ろに文字が続く場合は Val と通常の型変換関数で結果が分かれます。

Markdown
"123abc" / Val       => 123 [Double]
"123abc" / IsNumeric => False [Boolean]
"123abc" / CInt      => Error 13: 型が一致しません。
"123abc" / CDbl      => Error 13: 型が一致しません。

Val("123abc")123 を返しました。これは Val が先頭から読める数値部分だけを読むためです。

一方、IsNumeric("123abc")False です。IsNumeric は文字列全体を数値として評価できるかを見るので、後ろに abc が付いている文字列を数値とは判定しません。

CInt("123abc")CDbl("123abc") は、どちらも Error 13: 型が一致しません。 になりました。型変換関数は、Val のように先頭だけを都合よく拾う用途ではありません。入力全体を数値として変換したいときに使うものです。

実務での選び方

セルや入力欄から来た値を数値にしたいなら、「何をしたいのか」を先に決めてから選びましょう。

やりたいこと使う関数
数値として評価できるか判定したいIsNumeric
整数の Integer に変換したいCInt
小数を含む Double に変換したいCDbl
文字列の先頭から読める数値部分を取り出したいVal

数値として扱えるか確認したいだけなら、IsNumeric を使います。

VB
If IsNumeric(textValue) Then
    numberValue = CDbl(textValue)
End If

この形なら、判定と変換の役割が分かれます。IsNumeric は変換を担当せず、CDbl が変換を担当します。IsNumeric(textValue)True でも、数値として使うには CIntCDbl などで変換する処理が別に必要です。

整数が必要なら CInt です。ただし、CIntInteger の範囲、つまり -32,768 から 32,767 に収まる値を対象にし、小数は丸めます。小数を切り捨てたいだけなら、CInt を何となく使うより、丸め方を別に考えたほうが事故が少ないです。

小数を残すなら CDbl を使います。"123.5" のような値をそのまま計算に使いたいなら、CInt ではなく CDbl です。

文字列の先頭にある数値だけを読みたいなら Val が使えます。たとえば "123abc" から 123 を取りたいなら合います。ただし、入力全体が数値として正しいかを確認したい場面で Val を使うと、後ろの文字を見落とします。

tomo

先頭の数字だけ拾えてしまうのが、Valの怖いところです。入力チェックのつもりで使うと、後ろの文字を見逃します。

つまり、入力チェックでは IsNumeric、数値化では CIntCDbl、先頭部分の読み取りでは Val と分けます。ここを混ぜると、変換できたつもりなのに文字が残っていたり、残すべき小数が整数に丸められたりします。

まとめ

VBAで文字列を数値に変換するときは、関数名よりも役割で選ぶと整理しやすくなります。

IsNumeric は数値にできるかの判定、CIntInteger への変換、CDblDouble への変換、Val は文字列の先頭から読める数値部分の取得です。

"123" のような文字列なら差が見えにくいですが、"123.5""123abc" で挙動が変わります。小数を残したいなら CDbl、整数へ丸めたいなら CInt、文字列全体を数値として見たいなら IsNumeric で判定し、先頭の数値部分だけを拾うなら Val を使う。そこを分けておくと、変換まわりのバグを減らせます。

迷ったときは、「判定」「整数化」「小数を残す変換」「先頭部分の読み取り」のどれなのかを決めることをおすすめします。

よかったらシェアしてね!
  • URLをコピーしました!
目次