Inconsistent Formula Detector: Find the One Cell That Doesn't Match Its Neighbors
Scans every column for formula cells whose R1C1 pattern doesn't match the column's dominant formula. Flags the one cell that was manually altered, copy-pasted wrong, or broken during a restructure — and writes a clickable report. Does not modify the original data.
Table of Contents
TL;DR: A depreciation schedule has 500 rows in column F, all using =E{row}*0.2. Except row 347 — someone changed it to =E347*0.15. The numbers are close enough that the cross-foot still works, but the methodology is inconsistent. This macro finds that one cell by comparing every formula in a column against the dominant R1C1 pattern, flags the mismatches, and writes a clickable report sheet. Your original workpaper stays untouched.
The Problem
You’re reviewing the Henderson fixed asset schedule before filing. Column F
should be 20% of column E — MACRS 5-year, straight-line convention. Every row
uses =E{row}*0.2. The cross-foot ties to the trial balance. The partner signs
off.
Six weeks later, during the Q1 estimate calculation, you notice it: row 347 —
a 2019 addition — uses =E347*0.15. Not 20%. Fifteen percent. The preparer
manually adjusted one asset’s depreciation rate because the GL showed a
different number, and instead of investigating, they hard-coded the override
into the formula. The schedule footed because the dollar amount was small
enough. But it was wrong — and you signed the return based on it.
Inconsistent formulas in a column of identical calculations are the most subtle
Excel errors in tax workpapers. They don’t produce #REF! or #N/A. They
produce numbers — plausible, believable numbers that are just slightly wrong.
This macro finds them by comparing every formula in a column against the
dominant pattern. One InputBox, one click, and you know exactly which cells
don’t match.
#Prerequisites & Setup
What you’ll need:
- Excel 2016+ (desktop)
- A workpaper where at least one column contains multiple formulas
- The macro works on the active sheet or all sheets — your choice at runtime
What the macro does NOT do:
- It does not modify any cells on your original sheets. All output goes to a new “Inconsistent-Formulas” report tab.
- It does not tell you which formula is correct. It tells you which cells don’t match the majority — you decide whether the majority is right or the outlier is intentional.
- It does not flag hard-coded numbers. This macro compares formulas to formulas. For hard-coded numbers hiding in formula columns, use the Hard-Coded Number Detector.
Limitations:
- Only scans the UsedRange of each sheet — data outside that range is ignored
- A column needs at least 3 formula cells to establish a “dominant pattern.” Columns with 1–2 formulas are skipped.
- The dominant pattern must appear in at least 3 cells to be considered a pattern. If a column has 10 different formulas and no single formula appears 3+ times, the column is skipped.
- R1C1 pattern matching is structural, not semantic.
=A1+B1and=RC[-2]+RC[-1]match, but=A1+B1and=$A$1+B1do not (different references). Most inconsistent formulas in tax workpapers come from manual overrides, copy-paste errors, or restructuring — this catches all three. - Hidden sheets are skipped by default. See Adapt It to include them.
#The Macro
Option Explicit
Sub DetectInconsistentFormulas()
' ── Inconsistent Formula Detector ──────────────────
' Scans each column on the active sheet (or all
' sheets) for formula cells whose R1C1 pattern does
' NOT match the column's dominant formula.
' Writes a report to a new "Inconsistent-Formulas"
' sheet with hyperlinks back to each mismatch.
' Does NOT modify the original data.
'
' R1C1 notation normalizes row-relative references
' so =E5*0.2 and =E6*0.2 both become RC[-1]*0.2,
' making pattern comparison row-independent.
' ────────────────────────────────────────────────────
' ── Configuration ──────────────────────────────────
Const OUT_SHEET As String = "Inconsistent-Formulas"
Const MIN_FORMULA_CELLS As Long = 3
Const MIN_DOMINANT_COUNT As Long = 3
' ── State management ───────────────────────────────
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ── Variables ──────────────────────────────────────
Dim ws As Worksheet, wsOut As Worksheet
Dim cell As Range, rng As Range
Dim lastRow As Long, lastCol As Long
Dim outRow As Long, r As Long, c As Long
Dim scope As String
Dim flagged As Long, sheetsScanned As Long
Dim startWS As Worksheet
' ── Error handling ─────────────────────────────────
On Error GoTo CleanUp
Set startWS = ActiveSheet
' ── Scope selection ────────────────────────────────
scope = InputBox("Scan formulas on:" & vbCrLf & _
" Y = Active sheet only" & vbCrLf & _
" N = All sheets", _
"Scan Scope", "Y")
If scope = "" Then GoTo CleanUp
' ── Remove old report if it exists ─────────────────
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets(OUT_SHEET).Delete
Application.DisplayAlerts = True
On Error GoTo CleanUp
' ── Create report sheet ────────────────────────────
Set wsOut = ThisWorkbook.Worksheets.Add( _
After:=ThisWorkbook.Worksheets( _
ThisWorkbook.Worksheets.Count))
wsOut.Name = OUT_SHEET
' ── Write headers ──────────────────────────────────
wsOut.Range("A1:F1").Value = Array( _
"Sheet", "Cell", "Actual Formula", _
"Expected Pattern", "Dominant Count", "Suggestions")
wsOut.Range("A1:F1").Font.Bold = True
wsOut.Range("A1:F1").Interior.Color = RGB(50, 50, 50)
wsOut.Range("A1:F1").Font.Color = vbWhite
' ── Scan sheets ────────────────────────────────────
outRow = 2
flagged = 0
sheetsScanned = 0
Dim wsArr() As Worksheet
Dim i As Long, wsCount As Long
If UCase(Left(scope, 1)) = "Y" Then
wsCount = 1
ReDim wsArr(1 To 1)
Set wsArr(1) = ActiveSheet
Else
wsCount = 0
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> OUT_SHEET Then
If ws.Visible = xlSheetVisible Then
wsCount = wsCount + 1
End If
End If
Next ws
ReDim wsArr(1 To wsCount)
i = 0
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> OUT_SHEET Then
If ws.Visible = xlSheetVisible Then
i = i + 1
Set wsArr(i) = ws
End If
End If
Next ws
End If
For i = 1 To wsCount
Set ws = wsArr(i)
' Determine used range
Set rng = Nothing
On Error Resume Next
Set rng = ws.UsedRange
On Error GoTo CleanUp
If rng Is Nothing Then GoTo NextWS
lastRow = rng.Rows.Count + rng.Row - 1
lastCol = rng.Columns.Count + rng.Column - 1
sheetsScanned = sheetsScanned + 1
' ── Analyze each column ─────────────────────────
For c = rng.Column To lastCol
' ── Collect all formula R1C1 patterns ───────
Dim patterns As Object
Set patterns = CreateObject("Scripting.Dictionary")
For r = rng.Row To lastRow
Set cell = ws.Cells(r, c)
If cell.HasFormula Then
Dim r1c1 As String
r1c1 = Application.ConvertFormula( _
cell.Formula, xlA1, xlR1C1, , cell)
If Not patterns.Exists(r1c1) Then
patterns.Add r1c1, 1
Else
patterns(r1c1) = patterns(r1c1) + 1
End If
End If
Next r
' Skip if too few formula cells
Dim totalFormulas As Long
totalFormulas = 0
Dim key As Variant
For Each key In patterns.Keys
totalFormulas = totalFormulas + patterns(key)
Next key
If totalFormulas < MIN_FORMULA_CELLS Then GoTo NextCol
' ── Find dominant pattern ───────────────────
Dim dominantPattern As String
Dim dominantCount As Long
dominantPattern = ""
dominantCount = 0
For Each key In patterns.Keys
If patterns(key) > dominantCount Then
dominantPattern = key
dominantCount = patterns(key)
End If
Next key
' Skip if dominant is too weak
If dominantCount < MIN_DOMINANT_COUNT Then GoTo NextCol
' ── Flag mismatches ─────────────────────────
For r = rng.Row To lastRow
Set cell = ws.Cells(r, c)
If cell.HasFormula Then
r1c1 = Application.ConvertFormula( _
cell.Formula, xlA1, xlR1C1, , cell)
If r1c1 <> dominantPattern Then
' ── Sheet name ──────────────────
wsOut.Cells(outRow, 1).Value = ws.Name
' ── Hyperlinked cell address ────
Dim cellAddr As String
cellAddr = cell.Address(False, False)
wsOut.Hyperlinks.Add _
Anchor:=wsOut.Cells(outRow, 2), _
Address:="", _
SubAddress:="'" & ws.Name & _
"'!" & cellAddr, _
TextToDisplay:=cellAddr
' ── Actual formula in A1 ────────
wsOut.Cells(outRow, 3).Value = _
"'" & cell.Formula
' ── Expected pattern in A1 ──────
Dim expectedA1 As String
expectedA1 = Application.ConvertFormula( _
dominantPattern, xlR1C1, xlA1, , cell)
wsOut.Cells(outRow, 4).Value = _
"'" & expectedA1
' ── Dominant count ──────────────
wsOut.Cells(outRow, 5).Value = _
dominantCount & " of " & totalFormulas
' ── Suggestion ──────────────────
Dim suggestion As String
suggestion = "This cell's formula differs " & _
"from the dominant pattern used by " & _
dominantCount & " other formula cells " & _
"in column " & ColLetter(c) & "."
wsOut.Cells(outRow, 6).Value = suggestion
' Highlight mismatch row
wsOut.Rows(outRow).Interior.Color = _
RGB(254, 240, 138)
flagged = flagged + 1
outRow = outRow + 1
End If
End If
Next r
NextCol:
Next c
NextWS:
Next i
' ── Format and summarize ───────────────────────────
If flagged > 0 Then
With wsOut
.Columns("A:F").AutoFit
.Columns("C").ColumnWidth = 60
.Columns("D").ColumnWidth = 55
.Columns("F").ColumnWidth = 60
.Range("A1").Select
ActiveWindow.FreezePanes = True
.Rows(1).AutoFilter
End With
MsgBox "Found " & flagged & " inconsistent formula(s) " & _
"across " & sheetsScanned & " sheet(s)." & vbCrLf & vbCrLf & _
"See '" & OUT_SHEET & "' sheet for details. " & _
"Click any cell address to jump to the mismatch.", _
vbExclamation, "Formula Inconsistencies Found"
Else
MsgBox "No inconsistent formulas found across " & _
sheetsScanned & " sheet(s)." & vbCrLf & vbCrLf & _
"All formula columns use consistent patterns.", _
vbInformation, "All Clear"
End If
CleanUp:
If Not startWS Is Nothing Then
On Error Resume Next
startWS.Activate
On Error GoTo 0
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description, _
vbCritical, "Macro Error"
End If
End Sub
' ── Helper: Convert column number to letter ────────────
Private Function ColLetter(colNum As Long) As String
ColLetter = Split(Cells(1, colNum).Address(True, False), "$")(0)
End Function
#How It Works
#R1C1 notation is the secret weapon
The macro doesn’t compare A1-notation formulas directly. It converts every
formula to R1C1 notation first using Application.ConvertFormula:
r1c1 = Application.ConvertFormula( _
cell.Formula, xlA1, xlR1C1, , cell)
In R1C1 notation, references are relative to the cell containing the formula.
=E5*0.2 becomes =RC[-1]*0.2 — “the cell one column to my left, times 0.2.”
=E6*0.2 becomes the same thing: =RC[-1]*0.2. Even though the A1 formulas
reference different rows, their R1C1 patterns are identical because both
reference the cell one column to the left in their own row.
This means the macro compares structure, not literal text. A column of 500 cells where every formula says “the cell to my left times 0.2” will all match regardless of which row they’re in. The one cell that says “the cell to my left times 0.15” won’t match — and that’s exactly what you want.
Without R1C1 conversion, you’d have to strip out row numbers with complex
string parsing. With it, ConvertFormula does the heavy lifting in one call.
#Dictionary groups identical patterns
The macro uses a Scripting.Dictionary to count how many times each unique R1C1
pattern appears in a column:
Dim patterns As Object
Set patterns = CreateObject("Scripting.Dictionary")
For r = rng.Row To lastRow
Set cell = ws.Cells(r, c)
If cell.HasFormula Then
r1c1 = Application.ConvertFormula(...)
If Not patterns.Exists(r1c1) Then
patterns.Add r1c1, 1
Else
patterns(r1c1) = patterns(r1c1) + 1
End If
End If
Next r
After this pass, patterns contains every unique R1C1 formula used in the
column, mapped to its count. =RC[-1]*0.2 → 498, =RC[-1]*0.15 → 1,
=RC[-1]*0.2+100 → 1. The dominant pattern is clearly =RC[-1]*0.2 with 498
occurrences, and the other two are outliers.
#Two thresholds prevent false positives
The dominant pattern must satisfy two conditions before the column is scanned for mismatches:
-
At least
MIN_FORMULA_CELLSformula cells total (default: 3). A column with one formula and 99 constants isn’t a formula column — there’s no pattern to break. -
At least
MIN_DOMINANT_COUNToccurrences of the dominant pattern (default: 3). If a column has 5 formulas and all 5 are different, there’s no “dominant” pattern to compare against. The column might be genuine (e.g., a notes column where formulas are ad-hoc) and flagging everything would be noise.
These thresholds mean the macro stays silent on columns where inconsistency is expected. It only speaks up when there’s a clear pattern and a clear break in that pattern.
#The report shows both actual and expected formulas
Each flagged row has two formula columns:
wsOut.Cells(outRow, 3).Value = "'" & cell.Formula ' Actual
wsOut.Cells(outRow, 4).Value = "'" & expectedA1 ' Expected
The actual formula is what’s in the cell. The expected formula is the dominant
pattern, converted back to A1 notation for readability. If the dominant
pattern is =RC[-1]*0.2 and the flagged cell is E347, the expected formula
shows =E347*0.2 — exactly what the preparer should have entered.
The leading apostrophe (') prevents Excel from trying to evaluate the formula
string when you click on the report cell. It also keeps the formula text
left-aligned so it’s easy to read at a glance.
#The suggestion column gives context
suggestion = "This cell's formula differs from the dominant pattern " & _
"used by " & dominantCount & " other formula cells in column " & _
ColLetter(c) & "."
Instead of just listing the mismatch and leaving the preparer to count, the suggestion column tells them exactly how many other cells use the correct pattern. “498 of 500” is a lot more compelling than just “mismatch” — it tells the preparer that this is almost certainly a bug, not an intentional override.
#Why the report has an AutoFilter, not a sort
.Rows(1).AutoFilter
The report doesn’t sort results — it applies an AutoFilter instead. The mismatches appear in scan order (sheet by sheet, column by column, row by row), which preserves the natural context of the workpaper. The preparer can filter by sheet name to focus on one workpaper at a time, or scan the unfiltered list to see the full picture. Sorting by something artificial (like formula text) would scramble this natural order and make the report harder to navigate.
#Adapt It
Get the next macro in your inbox
One copy-paste-ready macro recipe every two weeks. No spam, no VBA theory — just automation that saves you time.
Excel Macro Guy
Excel enthusiast · married to an accountant
I love Excel. My wife is an accountant. Every busy season, I watch her wrestle with workpapers and think "a macro could do that in half a second." So I build them. She tests them on real client data. What survives gets published here.