Hey,
I will divide the answer into 2 parts, synchronous and asynchronous FIFOs.
Synchronous FIFO:
This is the case where both producer and consumer are working on same clock frequency. Consider the EMPTY condition in a 8 location FIFO. Write pointer (WP) and read pointer (RP) are at same location (say 000). Now consider FULL case. Let us say that producer has written to all 8 locations and WP has rolled back to location 000 after writing to 111. At the same time RP is still at 000. FIFO is FULL and WP=RP. As you can see that WP=RP (or Wp-RP= 0)is condition for both FULL and EMPTY. Thus, to differentiate the 2 we need to know if it was almost full or almost empty some clocks before. Let us say WP-RP=6, at this point it is almost full and when WP-RP=0 occurs we know that it is FULL. You can think of vica versa.
ASYNCHRONOUS FIFO:
Both producer and consumer are working on different clock frequency. Here we never use almost full and almost empty. Using them might result in deadlock situation. Rather we use n+1 bit WP and RP pointers.
Best Regards,
Abhishek