8086 Primes O(1) Check

Checking primes efficiently in x86 asm

This topic is frequently discussed on the interwebs, however I propose a solution of time/space complexity of O(1).

EDIT: My Professor pointed out the flaws in logic, it takes exponential space :(.
Also Performance is typically dominated by memory access times, so this method sucks.

High-level Algorithm

(I will be using a single byte in this example for the sake of simplicity.)

Given that we want to check unsigned bytes, they can have a value between \(0\) and \(255\) inclusive (i.e., \(2^8\)). We can create a boolean array of size 256, where true represent a prime number, and vice-versa.

Python is used in the following examples:

          #  0      1     2     3      4
isPrime = [False, False, True, True, False, ...]

We can now check if a number is prime as follows:

isPrime[0] # False
isPrime[2] # True
isPrime[3] # True
isPrime[4] # False

There you have it! A O(1) algorithm…

I would say it is incredibly space inefficient! A boolean can be represented in one bit, however we use a full byte. [Actually python uses bool type which is 4 to 8 bytes long! even worse, but that is beside the point I’m making. (You could argue that we can use NumPy which is more space efficient)]

Bit optimization

Using a 0 and 1 sequence would decrease the space used. Specifically, the space used would be divided by 2^(sizeof(byte)), which in is x86 is 2^3. So 8 values will fit in each byte.

To clarify, instead of 256 bytes to represent each False/True value, we use 256/2^3 = 32 bytes.

In python it could be defined as:

# define it yourself.
# It should return 0 and 1, for non-primes and primes respectively
  def isPrime(number):
	pass

# bits = [0, 0, 1, 1, ...]
  bits = [isPrime(i) for i in range(0,256)]


# chunks =  [[0, 0, 1, 1, 0, 1, 0, 1],
#            [0, 0, 0, 1, 0, 1, 0, 0],
#                               ...]]
  chunks = [bits[i:i+8] for i in range(0, len(bits), 8)]

# is there a faster way? :)
# bytes = [53, 20, 81, 5, 4, 81, ...]
  bytes = [int(''.join(str(i) for i in chunk), 2) for chunk in chunks] 

I find using octave/Matlab far simpler (In fact I don’t enjoy python :D)

bits   = isprime(0:255)' % [0, 0, 1, 1, 0, ...]
bytes    = bit2int(bits,8) % [53, 20, 81, 5, 4, 81, ...]

Now we need first to access the byte, then choose the correct bit, which should be simple in python, so lets just do it in x86 directly.

x86 asm

First define the byte array at the end of your code (or data segment)

; Define the byte array
PRIMES DB 53, 20, 81, 5, 4, 81, 4, 20, 17, 65, 16, 64, 69, 20, 64, 1, 16, 80, 5, 4, 17, 4, 20, 1, 69, 0, 16, 1, 20, 65, 64, 16

Now lets say we want to check if CX is prime or not.

# the number we test in this example
MOV CX, 17

; Shifting by 3 equivilent to dividing by 8. 
; Used to index at byte level
; Now BX = 2
MOV BX, CX
SHR BX, 3

; BL = the byte, in our case PRIMES[2] = 81 ; 81 = 01010001
MOV BL, [PRIMES + BX]


; Last 3 bits used to index which bit of the byte
; In our case, 17 & 07 = 1
AND CL, 07 ; bit mask  0b111
; CL = 2. Increment to change to index that starts at 1
INC CL

; 81 << 2.
; we get 0 or 1 that represents if the number is prime or not.
; It is then moved to carry bit.
SHL BL, CL

JC PRIME

PRIME:
NOP ; anything :)

Contact me if you know of a faster way or can use less than two registers!