The header of the HyperLogLog object is as follows:
struct hllhdr {
char magic[4]; /* "HYLL" */
uint8_t encoding; /* HLL_DENSE or HLL_SPARSE. */
uint8_t notused[3]; /* Reserved for future use, must be zero. */
uint8_t card[8]; /* Cached cardinality, little endian. */
uint8_t registers[]; /* Data bytes. */
};
Note the card field: it contains the last cardinality evaluated by the algorithm. Calculating an estimation of the cardinality is an expensive operation, so Redis caches the value and keeps it in this field.
When PFADD is called, the HyperLogLog object may be updated or not (there is a good probability it is not). If it is not updated, calling PFCOUNT will reuse the cached value (card field). If it is updated, the card field is invalidated, so the next PFCOUNT will execute the counting algorithm, and write the new value in the card field.
That's why the PFCOUNT can alter the HyperLogLog object.