## Verifying the check digit in PNRs using SAS

Swedish personal identification numbers (PNRs) comprise 10 digits (12 if century of birth is included). The first 6 digits represent date of birth (YYMMDD), the ninth digit represents gender (even for females and odd for males), and the tenth digit is a check digit which can be constructed from the preceding nine digits.

SAS code for verifying PNRs is shown below. This code can be downloaded here.

### Algorithm for calculating the check digit

First multiply each of the first nine digits in the PNR by the digits 2,1,2,1,2,1,2,1,2 and calculate the cumulative sum of each of these 9 calculations. If the product results in a number greater than 10, then add the individual digits. For example, 9 times 2 = 18, so we add 9 (the sum of 1 and 8) to the cumulative sum in the example below.

3 | 1 | 0 | 3 | 1 | 7 | 0 | 9 | 9 | |

2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | |

6 | 1 | 0 | 3 | 2 | 7 | 0 | 9 | 18 | 37 |

In the example above, the cumulative sum is 37. The check digit is the number we would have to add to the product sum in order to obtain a multiple of 10. In the above example, we would need to add 3, so the check digit is 3. If the cumulative sum is a multiple of 10 then the check digit is 0.

### SAS code for verifying the check digit

/******************************************* PNR_CHK.SAS This code reads 10-digit person numbers and checks that the check digit (the 10th digit in the PNR) is correct. It is assumed that PNR is a character variable of length 10. Paul Dickman (paul.dickman@mep.ki.se) September 1999 *******************************************/ /******************* Read some test data ********************/ data temp; input pnr $ 1-10; cards; 3103170993 3103170999 6812241450 6812241457 ; run; data pnr_chk; set temp; length product $ 18 result $ 3; array two_one {9} (2 1 2 1 2 1 2 1 2); /********************************************* multiply each of the first 9 digits in PNR by the corresponding digit in the array two_one and concatenate the result. The COMPRESS function removes blanks. *********************************************/ do i = 1 to 9; product=compress(product||(substr(pnr,i,1) *two_one{i})); end; /** Now we sum the digits **/ do i = 1 to length(product); sum=sum(sum,substr(product,i,1)); end; /** extract the check digit from PNR **/ chk=substr(pnr,10,1); /** calculate the correct check digit **/ corr_chk=mod(10-mod(sum,10),10); if chk=corr_chk then result='ok'; else result='bad'; label chk='Actual check number' corr_chk='Correct check number' pnr='Personnummer (10 digits)' ; run; proc print data=pnr_chk; var pnr chk product sum corr_chk result; run;