ExcelのワークシートではISODD・ISEVENで簡単に奇数・偶数判定ができるのに、いざVBAマクロで同じように書くと「定義されていません」とエラーになってしまう——本記事では、その理由とVBAでの正しい奇数・偶数判定の書き方を、実務でそのまま使えるサンプルコード付きで詳しく解説します。
VBAでISODD・ISEVENが「定義されていません」になる理由
まず押さえておきたいのは、ISODD・ISEVENは「ワークシート関数」であり、「VBAの組み込み関数」ではないという点です。
たとえば、VBAのコードに次のように書くとします。
Sub Sample()
Dim v As Long
v = 10
' これはエラーになる
If ISEVEN(v) Then
MsgBox "偶数です"
End If
End Sub
このコードを実行すると、「コンパイルエラー: Sub または Function が定義されていません」というエラーになります。理由は簡単で、VBE(VBAの編集画面)から見たときに、ISEVENやISODDという名前の関数は存在しないからです。
ところが、Excelのセル上では、
=ISEVEN(A1)=ISODD(A1)
のように普通に使えます。これは「ワークシート関数」としてExcel側に用意されているためで、VBAから利用するときには「WorksheetFunction」を経由して呼び出す必要がある、もしくはVBAだけで完結する別の書き方(Mod演算)を使う必要がある、というわけです。
偶数・奇数判定の2つの代表的なアプローチ
VBAで偶数・奇数を判定する方法は大きく分けて次の2つです。
| 解決アプローチ | 内容 | サンプルコード(抜粋) | メモ |
|---|---|---|---|
| WorksheetFunction経由で呼び出す | Application.WorksheetFunction.IsEven / IsOdd を使い、ワークシート関数ISODD・ISEVENをVBAから利用する | If WorksheetFunction.IsEven(対象値) Then | ワークシート関数の動きをそのまま使いたいときに便利 |
| 自前で剰余演算を使う | 値 Mod 2 = 0 なら偶数、値 Mod 2 <> 0 なら奇数と判定する | Dim isEven As Boolean | Excel関数に依存せず、シンプルかつ高速 |
どちらを使っても正しく判定できますが、「Excel関数の動きに合わせたい」ならWorksheetFunction、「処理速度とシンプルさを優先」するならMod演算というイメージで使い分けるとスッキリします。
WorksheetFunction.IsEven / IsOdd を使う方法
基本構文
ワークシート関数ISEVEN/ISODD をVBAから使うには、WorksheetFunctionオブジェクトを経由して呼び出します。
If Application.WorksheetFunction.IsEven(値) Then
' 偶数のときの処理
Else
' 奇数のときの処理
End If
If WorksheetFunction.IsOdd(値) Then
' 奇数のときの処理
End If
Application.は省略しても動作しますが、明示的に書いておくと可読性が上がるのでおすすめです。WorksheetFunctionを付け忘れてIsEvenとだけ書くと、「定義されていません」というコンパイルエラーになります。
代表的な実装例(行の値を判定して書き出す)
以下は、指定列(ここでは7列目)の値が偶数か奇数かを判定し、結果を9列目に書き出していくサンプルです。質問文にあった例に少しだけ手を入れ、コメントを追加しています。
Sub MacroTest()
Dim rowIdx As Long
Dim ans As String
rowIdx = 2 ' 見出し行を飛ばして2行目から開始
While rowIdx < 15
' 7列目 (G列) の値が偶数かどうかを判定
If WorksheetFunction.IsEven(Cells(rowIdx, 7).Value) Then
ans = "偶数"
Else
ans = "奇数"
End If
' 判定結果を 9列目 (I列) に出力
Cells(rowIdx, 9).Value = ans
rowIdx = rowIdx + 1
Wend
End Sub
この書き方のポイントは次の通りです。
- WorksheetFunction.IsEven を使うことで、「セルの数値に対してISEVENを実行した結果」をVBAでそのまま利用できる。
- セルが空白や文字列でも、ワークシート関数と同じ挙動(エラーやFALSEなど)を期待できる。
- VBA7 / 64bit環境でも問題なく動作する一般的な書き方です。
WorksheetFunctionを使うときの注意点
WorksheetFunctionを使うときによくあるつまずきと対策をまとめておきます。
| ポイント | 内容 | 対策例 |
|---|---|---|
| 空白・文字列の扱い | 空セルや文字列に対して実行すると、ワークシート関数同様にエラーになる場合があります。 | IsNumericで事前チェックを入れる。 |
| 小数の扱い | 小数をそのまま渡すと、ExcelのISODD/ISEVENの仕様に従って判定されるため、意図と違う結果になる場合があります。 | CLngやIntで整数化してから渡す。 |
| エラー処理 | 不正な値が混ざっていると実行時エラーで止まってしまう可能性があります。 | エラーをトラップして、エラー時の処理(スキップ・空欄・固定文字など)を決めておく。 |
エラーに強い書き方の一例
実務でよくある「数値以外も混ざっているリスト」に対して奇数・偶数を判定する場合の、少し丁寧な書き方を紹介します。
Sub MacroTestSafe()
Dim rowIdx As Long
Dim v As Variant
Dim ans As String
rowIdx = 2
While rowIdx < 15
v = Cells(rowIdx, 7).Value
If IsNumeric(v) Then
' 整数に丸めてから判定(小数が来ても安全)
If WorksheetFunction.IsEven(CLng(v)) Then
ans = "偶数"
Else
ans = "奇数"
End If
ElseIf IsEmpty(v) Then
ans = "" ' 空白はそのまま空欄にするなど、ルールを決める
Else
ans = "数値以外"
End If
Cells(rowIdx, 9).Value = ans
rowIdx = rowIdx + 1
Wend
End Sub
こうしておくと、「空白」「文字列」「小数」「整数」が混在していても、マクロが途中で止まらず、判定結果をわかりやすく書き出すことができます。
Mod演算子でシンプルに奇数・偶数判定する方法
Mod演算子での基本判定ロジック
VBAだけで完結させたい場合には、剰余演算子 Mod を使うのがもっともシンプルです。
値 Mod 2 = 0… 偶数値 Mod 2 <> 0… 奇数
たとえば、次のように書けます。
Sub ModSample()
Dim n As Long
n = 10
If n Mod 2 = 0 Then
MsgBox "偶数です"
Else
MsgBox "奇数です"
End If
End Sub
Mod演算はVBAの組み込み機能なので、Excel関数に依存せず高速に判定できるのが利点です。また、負の整数や0についても問題なく判定できます(0は偶数です)。
汎用的な判定関数として切り出す
同じ判定ロジックを何度も書くのは非効率なので、ユーザー定義関数(UDF)として共通化しておくのがおすすめです。
Public Function IsEvenNumber(ByVal value As Long) As Boolean
IsEvenNumber = (value Mod 2 = 0)
End Function
Public Function IsOddNumber(ByVal value As Long) As Boolean
IsOddNumber = (value Mod 2 <> 0)
End Function
これで、VBAのどこからでも次のように呼び出せます。
Sub ModFunctionSample()
Dim i As Long
For i = -5 To 5
If IsEvenNumber(i) Then
Debug.Print i & " は偶数"
Else
Debug.Print i & " は奇数"
End If
Next i
End Sub
特にループ処理の中で何度も判定するようなケースでは、Modでの判定はWorksheetFunctionよりも軽量なので、処理速度が気になるマクロではこちらを優先して使うと良いでしょう。
Variantやセルの値を受け取る関数例
実際のマクロでは、「セルの値が整数とは限らない」「空白や文字も来る」といった状況が多いので、Variantを受け取る判定関数にしておくと扱いやすくなります。
Public Function EvenOddLabel(ByVal value As Variant) As String
' 数値以外は空文字を返す(ルールは用途に応じて変更)
If Not IsNumeric(value) Then
EvenOddLabel = ""
Exit Function
End If
' 小数が来た場合は整数部分で判定したいなど、方針を決める
' ここでは四捨五入してから判定する例
Dim n As Long
n = CLng(value)
If n Mod 2 = 0 Then
EvenOddLabel = "偶数"
Else
EvenOddLabel = "奇数"
End If
End Function
この関数を使えば、次のようにVBAから利用できます。
Sub UseEvenOddLabel()
Dim rowIdx As Long
For rowIdx = 2 To 50
Cells(rowIdx, 9).Value = EvenOddLabel(Cells(rowIdx, 7).Value)
Next rowIdx
End Sub
また、標準モジュールに置いておけば、セル上でも=EvenOddLabel(G2)のように関数として利用できるので、「VBAでもワークシートでも同じ関数名で使いたい」ときにも便利です。
WorksheetFunction と Mod の違いを整理
ここまでの内容を踏まえて、WorksheetFunction.IsEven/IsOddとModを使った判定の違いを表にまとめます。
| 項目 | WorksheetFunction.IsEven / IsOdd | Mod演算(値 Mod 2) |
|---|---|---|
| 依存関係 | Excelのワークシート関数に依存 | VBAのみで完結 |
| 書き方のシンプルさ | WorksheetFunctionの指定が必要 | 1行の式で判定可能 |
| 処理速度 | 関数呼び出し分のオーバーヘッドあり | 比較的高速(大量ループに向く) |
| Excelと同じ仕様を使いたい場合 | ◎(セルでの結果と揃えやすい) | ×(自分で仕様を決めて実装する) |
| 学習コスト | WorksheetFunctionの使い方を覚える必要あり | Mod演算の意味さえ分かればOK |
| おすすめ用途 | 「セルのISEVEN/ISODDと同じ判定がしたい」場合 | 高速処理・シンプルさ重視の業務マクロ |
用途別・どちらを使うべきかの目安
実務でありがちなシチュエーションごとに、どちらの方法を選ぶとよいかの目安を挙げておきます。
| シチュエーション | 推奨方法 | コメント |
|---|---|---|
| 既にセルでISEVEN/ISODDを使っており、VBAでも同じ結果に揃えたい | WorksheetFunction.IsEven / IsOdd | セルの動作と完全一致させたい場合はWorksheetFunction一択。 |
| 数万〜数十万行をループして判定する重い処理 | Mod演算 | 処理速度を考えるとModの方が有利。UDF化しておくと再利用性も高い。 |
| ロジックをVBAと他言語(Pythonなど)で共通化したい | Mod演算 | どの言語でも「n % 2」のような剰余演算は共通。移植性を考えると◎。 |
| VBA初心者向けの教育用サンプル | Mod演算 | 「2で割って余りが0なら偶数」という説明は直感的で理解しやすい。 |
奇数・偶数判定の実務的な活用例
行番号の偶数行・奇数行で処理を分ける(縞模様・色分け)
もっともよく使うパターンは、「行番号が偶数か奇数かで処理を変える」ケースです。たとえば、一覧表の偶数行にだけ背景色を塗る「ゼブラ行」をVBAで実装する例です。
Sub ColorAlternateRows()
Dim lastRow As Long
Dim r As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For r = 2 To lastRow
If r Mod 2 = 0 Then
' 偶数行だけ薄いグレーを塗る
Rows(r).Interior.ColorIndex = 15
Else
' 奇数行は塗りつぶしなしに戻す
Rows(r).Interior.ColorIndex = xlNone
End If
Next r
End Sub
このように、行番号と組み合わせる場合は、WorksheetFunctionを使わずにModだけで十分です。
明細番号・伝票番号のルールチェック
業務システムによっては、「奇数番号が売上、偶数番号が返品」のような運用ルールが決まっていることがあります。その場合、入力された伝票番号の奇数・偶数をチェックして、誤入力を防ぐことができます。
Function IsValidSlipNo(ByVal slipNo As Variant, ByVal isReturn As Boolean) As Boolean
If Not IsNumeric(slipNo) Then
IsValidSlipNo = False
Exit Function
End If
Dim n As Long
n = CLng(slipNo)
If isReturn Then
' 返品なら偶数番号であること
IsValidSlipNo = (n Mod 2 = 0)
Else
' 通常売上なら奇数番号であること
IsValidSlipNo = (n Mod 2 <> 0)
End If
End Function
フォームの「登録」ボタンにこの関数を組み合わせれば、番号ルールの自動チェックが簡単に実装できます。
偶数月・奇数月だけ処理する
奇数月のみ、偶数月のみ処理を行いたい場合にも、Month関数とModを組み合わせればシンプルに書けます。
Sub ProcessOddMonths()
Dim targetDate As Date
Dim m As Long
targetDate = Date ' 今日の日付
m = Month(targetDate)
If m Mod 2 <> 0 Then
MsgBox "奇数月なので処理を実行します。"
' ここに処理を書く
Else
MsgBox "偶数月なので今回はスキップします。"
End If
End Sub
年月を扱う処理は一見複雑になりがちですが、月番号は1〜12の整数なので、Mod 2でサクッと奇数月・偶数月を判定できます。
よくあるつまずきポイントと対処法
IsEven/IsOddをそのまま書いてコンパイルエラー
もっともありがちなミスは、次のようにIsEvenだけ書いてしまうパターンです。
' コンパイルエラーになる例
If IsEven(Cells(2, 1).Value) Then
' ...
End If
この場合の対処法は2つだけです。
- ワークシート関数を使いたい →
WorksheetFunction.IsEvenと書く - VBAだけで判定したい →
Cells(2, 1).Value Mod 2 = 0のように書く
混乱する原因は、「セル上の関数名」と「VBAの関数名」が同じに見えることです。VBAからワークシート関数を使う場合は必ずWorksheetFunctionを経由するというルールだけ覚えておくと、かなりスッキリします。
Double型や小数の扱いで意図しない結果になる
計算結果などを直接渡すと、Double型(小数)になっていることがよくあります。小数をどう扱うかは業務ロジック次第なので、「判定前に整数化する方針」を決めておくと安全です。
よく使うパターンとしては次のようなものがあります。
- 小数切り捨てで判定したい →
Int(value)またはFix(value) - 四捨五入で判定したい →
CLng(value)
たとえば、「小数点以下は四捨五入してから偶数・奇数を判定する」というルールなら、次のように書けます。
Dim v As Double
v = 10.6
If CLng(v) Mod 2 = 0 Then
Debug.Print "偶数として扱う"
Else
Debug.Print "奇数として扱う"
End If
WorksheetFunctionを使う場合でも、渡す前に一度整数化してしまえば、「小数をどう扱うか」を自分でコントロールできます。
負の数の扱いが不安なとき
VBAのModは負の数も扱えます。偶数・奇数判定をするときは、「余りが0かどうか」だけ見ればよいので、負の数でも問題ありません。
-4 Mod 2 = 0→ 偶数-3 Mod 2 = -1→ 0ではないので奇数
つまり、判定ロジックは正の数とまったく同じでOKです。負の値を扱う可能性がある処理でも、n Mod 2 = 0という書き方でそのまま使えます。
ISODD・ISEVENをVBAで使いこなすための実践的テクニック
共通モジュールに「判定ラッパー関数」を用意する
プロジェクト全体で何度も奇数・偶数判定を使う場合は、「内部ではModを使うけれど、見た目はIsEven/IsOdd的」なラッパー関数を作っておくと便利です。
Public Function IsEvenValue(ByVal value As Variant) As Boolean
If Not IsNumeric(value) Then
IsEvenValue = False
Exit Function
End If
IsEvenValue = (CLng(value) Mod 2 = 0)
End Function
Public Function IsOddValue(ByVal value As Variant) As Boolean
If Not IsNumeric(value) Then
IsOddValue = False
Exit Function
End If
IsOddValue = (CLng(value) Mod 2 <> 0)
End Function
こうしておけば、各所のコードは次のようにスッキリ書けます。
If IsEvenValue(Cells(i, 1).Value) Then
' 偶数のときの処理
End If
内部の実装(Modで判定するのか、WorksheetFunctionを使うのか)はあとから差し替えられるので、運用フェーズで仕様を変えたくなったときにも柔軟に対応できます。
WorksheetFunctionバージョンとの切り替えも可能
もし「セルのISODD/ISEVENと完全に揃えたい」場合は、先ほどのラッパー関数の中身だけ、WorksheetFunctionを使うように差し替えればOKです。
Public Function IsEvenValue(ByVal value As Variant) As Boolean
On Error GoTo ErrHandler
IsEvenValue = Application.WorksheetFunction.IsEven(value)
Exit Function
ErrHandler:
' エラー時の扱いはルールに応じて決める
IsEvenValue = False
End Function
このように、外から見ると同じ関数名・同じ引数にしておくと、「Mod版」⇔「WorksheetFunction版」を切り替えても、呼び出し側のコードをいじる必要がありません。
まとめ:VBAでの奇数・偶数判定は2パターン覚えれば十分
本記事では、Excelのワークシート関数ISODD・ISEVENがVBAでそのまま使えない理由と、VBAで偶数・奇数を判定する2つの代表的な方法を解説しました。
- WorksheetFunction.IsEven / IsOdd
→ ワークシート関数の挙動をそのまま使いたいときに最適。
→WorksheetFunctionプレフィックスを忘れないことが重要。 - Mod演算(値 Mod 2)
→ Excel関数に依存せず、シンプルかつ高速。
→ 共通の判定関数としてUDF化しておくと大規模マクロでも使いやすい。
実務では、「セルの関数と結果を揃えたいか」「処理速度やシンプルさを優先したいか」という観点で使い分けると、迷うことが少なくなります。どちらの書き方も一度手を動かしてサンプルを作っておくと、次に同じような要件が出てきたときにすぐ流用でき、日々のExcel作業の自動化がさらに効率的になります。

コメント