The data sheet recommends that you also look at
https://ww1.microchip.com/downloads/en/devicedoc/33023a.pdf and in particular Section 12.
I think the problem you are encountering is that the formula use are using has a lot of integer divisions. These will round down and so your calculation will be on the 'fast' side. Also be aware of the 'natural size' of integers in your compiler. Many use 16-bit integers for constants by default so your "8000000" for the 'cpu_freq' could be seen as 4608 (only the bottom 16 bits will be used).
Personally I would 'pre-calculate' the 'cpu-freq / cycle_pulses / pulse_state' part (and yes, bring 'pulse-state' right up there) and go with that.
I don't understand how you are using the 'freq' variable. When you calculate the prescalar, you seem to be treating it as though it has units of kHz, but for the timer register calculation you are back to Hz (because your 'cpu-freq' is in Hz).
Remember that these 'old' style timers count UP and setting the interrupt flag occurs when they roll over from 0xffff to 0x0000 (in the case of Timer1 on your MCU). Therefore if you want it to trigger after (say) 20 clocks (and taking into account all of the prescaler values etc) then you need to set TMR1H/L to 65535 - 20 = 65515.
Also if you are trying to reset the timer registers in the ISR (or elsewhere to get a repetitive clock) remember that the timer will keep ticking while you enter the ISR. Therefore you need to take these 'extra' counts into account, normally by adding the 'current' count from your calculated value. (I.e. if you want to load the above 65515, then the timer registers may already be a (say) 3 to you need to put 65515 + 3 = 65518 back into the register.)
I don't know the compiler you are using but generally it is MUCH simpler to set the (for example) prescalar bits using "T1CONbits.T1CKPS = 0b10;" (check for the correct syntax in your user guide).
Susan