Option Explicit

'===========================================================
' 部品_値変換_実数 / ConvertToDouble
'   ・値（Variant）を、実数（Double：小数を含む数）に変換して返す
'     → Converts a value (Variant) to a Double (a number that may have a fraction) and returns it.
'
'   ・変換できない値は、False や空の値ではなく、原因を示すエラーで止まる（Err.Raise）
'     → A value that cannot be converted stops with an error naming the cause (no silent wrong value).
'===========================================================

'===========================================================
' ■区分 / Classification
'   ・基礎区分：土台 ／ 用途区分：取得 ／ 操作対象区分：セル（セルから取得した値を扱う）
'     → Base: foundation / Purpose: get / Target: cell (works on a value read from a cell)
'
' ■バージョン / Version
'   ・Version 1.0（2026-10-29）
'===========================================================

'===========================================================
' ■URL（WordPress部品ページ）
'   https://feelcodelab.com/vba_parts/converttodouble/
'===========================================================

'===========================================================
' ■前提条件（AI誤動作防止の最重要項目） / Preconditions
'   ・【最重要】変換の前に、VLOOKUP値取得の戻り値が、見つからない（#N/A）場合でないかを、呼び出し側が確認する
'     → 確認の書き方：If IsError(v) Then MsgBox "見つかりませんでした": Exit Sub
'     → [MOST IMPORTANT] The caller checks IsError on the VLOOKUP result before converting.
'
'   ・数値（整数・小数）、および、数字として読める文字列（"1,000.5"、全角の "１２３"、前後の空白付きなど）を変換できる
'     → Numbers and text that reads as a number can be converted.
'
'   ・空白（Empty）・空文字・Null・エラー値（#N/A など）・日付・True/False・オブジェクト・配列は、変換できない値として、エラーで止まる（1018）
'     → Empty, empty text, Null, error values, dates, Booleans, objects and arrays stop with error 1018.
'
'   ・整数（Long）として受け取りたい場合は、値変換_整数を使う（別の部品。小数は、エラーになる）
'     → Use ConvertToLong (a separate component) when a whole number is required.
'
'   ・変換できない値は、原因を示すエラーで止まる（Err.Raise）。呼び出し側の起点で受けて、Err.Description を表示する
'       - 値が、実数に変換できない（数字として読めない・空白・Null・エラー値・日付・True/False など）（vbObjectError + 1018）
'     → Values that cannot be converted stop with an error naming the cause.
'
'   ・Offset / Find / Resize / With は禁止
'     → Offset, Find, Resize, and With statements are prohibited.
'===========================================================

'===========================================================
' ■依存関係（部品名明記） / Component Dependencies
'   ・他の部品を参照しない（土台）
'     → References no other component (foundation).
'
'   ・組み合わせ：VLOOKUP値取得 → 値変換_実数
'     （VLOOKUP値取得の戻り値を、IsError で確認してから、この部品で変換する）
'     → Typical combination: [GetVlookupValue] -> [ConvertToDouble]
'===========================================================

'===========================================================
' ■依存関係（シート・表） / Sheet Dependencies
'   ・特定のシートや表には、依存しない（値を受け取って、型を変換するだけ。VLOOKUP値取得などの戻り値（Variant）を想定している）
'     → Does not depend on any specific sheet or table; it only converts the value it receives (typically the result of [GetVlookupValue]).
'===========================================================

'===========================================================
' ■技術解説 / Technical Notes
'
'   ・変換を、VLOOKUP値取得の中に入れず、別の部品にする理由
'       → 迷ったら分ける。取得する値の型は、呼び出し側が決める。また、型の変換は、VLOOKUP 以外
'          （セルの値の読み取りなど）にも使える。
'       → Conversion is separate (when in doubt, split); it is also usable for values from other sources.
'
'   ・日付・True/False を、数値として扱わない理由
'       → 日付はシリアル値、True は -1 になるが、これは、意図した変換であることは少なく、取り違えの元になる。
'       → A date would become a serial number and True would become -1, which is rarely intended.
'
'   ・Double は、小数の計算に、わずかな誤差が出ることがある
'       → 金額の計算など、誤差が許されない場合は、呼び出し側で、丸めなどの対応を決める。この部品は、値を変換するだけで、丸めない。
'       → Double can carry tiny errors; the caller decides rounding. This component only converts.
'
'   ・変換できない値を、False や空の値で返さず、エラーで止める理由
'       → 値を返す部品は、戻り値で、失敗を表せない。誤った値のまま、処理が進まないように、原因を示すエラーで止める。
'       → A value-returning component cannot express failure in its return value, so it stops with an error.
'
'   ・On Error を使う箇所
'       → 文字列からの変換は、事前の確認をすり抜ける場合に備えて、その 1 行だけ On Error Resume Next で確認する（他は使わない）。
'===========================================================

'---------------------------------------------
' ■引数（型＋意味） / Arguments
'   vValue : 変換する値（Variant）。VLOOKUP値取得の戻り値や、セルの値
'            → The value to convert (Variant), e.g. the result of GetVlookupValue or a cell value.
'---------------------------------------------

'---------------------------------------------
' ■戻り値（型＋意味） / Return Value
'   Double : 変換した値
'            → The converted value.
'---------------------------------------------

'---------------------------------------------
' ■エラー / Errors (Err.Raise)
'   ・vbObjectError + 1018：値が、実数に変換できない（数字として読めない・空白・Null・エラー値・日付・True/False など）
'---------------------------------------------

'===========================================================
' ■禁止事項（AI向け） / Prohibitions
'   ・VLOOKUP値取得の戻り値を、IsError で確認せずに、この部品へ渡すことの禁止
'     → Do not pass the VLOOKUP result without checking IsError.
'
'   ・変換できない値を、エラーで止めずに、0 や空文字で代用することの禁止
'     → Do not substitute 0 or an empty string for a value that cannot be converted.
'
'   ・Offset禁止 / Find禁止 / Resize禁止 / With禁止
'     → Offset, Find, Resize, and With blocks are prohibited.
'===========================================================

'===========================================================
' ■使用例（サンプルコード） / Example
'   ◆VLOOKUP値取得の結果を変換する / Convert the result of GetVlookupValue◆
'   Dim v    As Variant
'   Dim rate As Double
'
'   v = 部品_VLOOKUP値取得(rng, "A-001", 5)
'   If IsError(v) Then MsgBox "見つかりませんでした": Exit Sub    '先に、見つからない場合を確認する
'
'   rate = 部品_値変換_実数(v)                                 '変換できない値は、エラーで止まる
'
'   ※ 変換できない値の場合は、原因を示すエラーで止まる（起点で On Error で受けて、Err.Description を表示する）
'===========================================================

'===========================================================
' ■ラッパー関数（日本語名） / Wrapper Function
'   ・辞書の日本語名で呼び出すための窓口
'     → Entry point callable by the Japanese dictionary name.
'
'   ・内部では英語名の本体処理を呼び出し、結果をそのまま返す
'     → Internally calls the English-named core function and returns its result as it is.
'===========================================================
Function 部品_値変換_実数(ByVal vValue As Variant) As Double

    '本体処理（英語名）を呼び出し、結果をそのまま返す / Call core function and return its result
    部品_値変換_実数 = f_ConvertToDouble(vValue)

End Function

'===========================================================
' ■本体処理（英語名） / Core Function
'   ・値（Variant）を、実数（Double：小数を含む数）に変換して返す
'     → Converts a value (Variant) to a Double (a number that may have a fraction) and returns it.
'===========================================================
Function f_ConvertToDouble(ByVal vValue As Variant) As Double

    Dim d As Double
    Dim s As String
    Dim errNo As Long

    f_ConvertToDouble = 0

    If IsObject(vValue) Or IsArray(vValue) Then
        Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                  "値が、オブジェクトまたは配列のため、実数（Double）に変換できません。"
    End If

    If IsError(vValue) Then
        Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                  "値が、エラー値（#N/A など）のため、実数（Double）に変換できません" & _
                  "（VLOOKUP値取得の戻り値は、先に IsError で確認してください）。"
    End If

    If IsNull(vValue) Then
        Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                  "値が Null のため、実数（Double）に変換できません。"
    End If

    If IsEmpty(vValue) Then
        Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                  "値が空白（Empty）のため、実数（Double）に変換できません。"
    End If

    '---------------------------------------------
    ' ■型ごとに、Double へ変換（文字列は、数字として読めるかを先に確認する）
    '   / Convert to Double by type (for text, check first that it reads as a number)
    '---------------------------------------------
    Select Case VarType(vValue)

        Case vbString
            s = Trim(vValue)
            If Len(s) = 0 Or Left(s, 1) = "&" Or IsNumeric(s) = False Then
                Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                          "文字列「" & Left(vValue, 50) & "」は、数字として読めないため、実数（Double）に変換できません。"
            End If

            On Error Resume Next
            d = CDbl(s)
            errNo = Err.Number
            On Error GoTo 0

            If errNo <> 0 Then
                Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                          "文字列「" & Left(vValue, 50) & "」は、大きすぎる（または小さすぎる）ため、実数（Double）に変換できません。"
            End If

        Case vbByte, vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
            d = CDbl(vValue)

        Case Else
            Err.Raise vbObjectError + 1018, "f_ConvertToDouble", _
                      "値の型（" & TypeName(vValue) & "）は、実数（Double）に変換できません" & _
                      "（日付・True/False は、数値として扱いません）。"

    End Select

    f_ConvertToDouble = d

End Function

