Skip to content

Instantly share code, notes, and snippets.

@cimnine
Last active July 29, 2021 22:01
Show Gist options
  • Save cimnine/7913819 to your computer and use it in GitHub Desktop.
Save cimnine/7913819 to your computer and use it in GitHub Desktop.
Luhn Algorithm in VBA (for example for use in Excel)
' Version 1.0.0
' You may use these functions directly in Excel: "=luhnCheck(A55)"
' probably only needed internally
Function luhnSum(InVal As String) As Integer
Dim evenSum As Integer
Dim oddSum As Integer
evenSum = 0
oddSum = 0
Dim strLen As Integer
strLen = Len(InVal)
Dim i As Integer
For i = strLen To 1 Step -1
Dim digit As Integer
digit = CInt(Mid(InVal, i, 1))
If ((i Mod 2) = 0) Then
oddSum = oddSum + digit
Else
digit = digit * 2
If (digit > 9) Then
digit = digit - 9
End If
evenSum = evenSum + digit
End If
Next i
luhnSum = (oddSum + evenSum)
End Function
' for the curious
Function luhnCheckSum(InVal As String)
luhnCheckSum = luhnSum(InVal) Mod 10
End Function
' true/false check
Function luhnCheck(InVal As String)
luhnCheck = (luhnSum(InVal) Mod 10) = 0
End Function
' returns a number which, appended to the InVal, turns the composed number into a valid luhn number
Function luhnNext(InVal As String)
Dim luhnCheckSumRes
luhnCheckSumRes = luhnCheckSum(InVal)
If (luhnCheckSumRes = 0) Then
luhnNext = 0
Else
luhnNext = ((10 - luhnCheckSumRes))
End If
End Function
@adamkalcic
Copy link

Hello,
I was trying to use this to method using 12 digit starting numbers and add 13th check digit for 13 total. When plugging one of the original numbers into this online calculator (http://planetcalc.com/2464/) it doesn't match up to the end result I get using this. Seems to be because it is an even number?

Using this number as an example starting number: 100070348504
This formula is telling me the check digit is: 7
The online calculator says it should be: 4

Any ideas?

@glahens
Copy link

glahens commented Nov 9, 2015

Small bug in this algorithm.. That's why it's not matching what you see at planetcalc.

The line If ((i Mod 2) = 0) Then
should actually be If ((i Mod 2) <> 0) Then
for these functions to be correct.

Someone should fix this.

@matthunt1984
Copy link

Glahens is right. I had a similar issue and the above suggestion resolved it. Thanks.

@shenzhonghua
Copy link

It works for me for ICCID validation, maybe there're varies here.

@Voidfae
Copy link

Voidfae commented Jun 20, 2017

There are a few problems with this implementation, though If ((i Mod 2) = 0) Then is not necessarily wrong.

First, when calculating the sum, we use i going from length down to 1. This means the first position is considered odd or even based on the length of InVal. First position should always be 1, odd.

(This would explain why If ((i Mod 2) <> 0) Then may yield correct result for some, it depends on the length of your numbers)

My solution:

   For i = 0 To strLen - 1 Step 1
        Dim digit As Integer
        digit = CInt(Mid(InVal, strLen - i, 1))

If ((i Mod 2) = 0) Then is unchanged, as we start at pos 1 when i = 0.

Also when calculating the next value, first position is considered to be held by the value you are trying to calculate.
Ie, if you want to calculate the check value x of 123456, then you need to think of the number as 123456x where x is position 1.
The code does not take this in to account.
Changing luhnCheckSumRes = luhnCheckSum(InVal) to luhnCheckSumRes = luhnCheckSum(InVal*10) in Function luhnNext(InVal As String)
seems to work.

@yakit4k0
Copy link

yakit4k0 commented Jul 12, 2017

Hello,
Using Excel 2016, Getting #VALUE! with Function luhnNext(InVal As String),
after I change the code to luhnCheckSumRes = luhnCheckSum(InVal*10) (suggested by Voidfae) and result of InVal*10 has more than 15 digits

I'm new to Excel & VBA & programming but a google search shows me that's because of floating point?

After a few try, since Function luhnNext(InVal As String) is As String,
instead of change the line to luhnCheckSumRes = luhnCheckSum(InVal*10),
I'm keeping it untouched, but add a new InVal = InVal + "0" line above that, seems working now.
Is this the right way to do it?

@rifat-f
Copy link

rifat-f commented Aug 9, 2019

The doubling is alternating, so why not just use a flag to alternate. This way it doesn't matter if the length of InVal is even or odd

    Dim i As Integer
    Dim ToDouble As Boolean
    For i = strLen To 1 Step -1
        Dim digit As Integer
        digit = CInt(Mid(InVal, i, 1))

        ToDouble  = Not ToDouble 
 
        If Not ToDouble Then
            oddSum = oddSum + digit
        Else
            digit = digit * 2
        

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment