Here's your code (trimmed down and tided a bit, but functionally identical):
$donation = get_option('ai_donation');
$d_pct = 1 + intval(round(intval($donation))); //adding the 1 is needed for some reason to obtain the correct percentage
$d_rand = mt_rand(1,100);
$d_range = range(1, $d_pct);
// use donation code
If you set a value of 5 for the donation percentage $d_pct ends up being 6. The if statement is then true.
Next a random number is picked between 1 and 100 inclusive. Then an array created between 1 and (using 5% as an example) 6 inclusive - so the array is [1, 2, 3, 4, 5, 6].
The array_search function then looks for the random number in the array. Since the array is 1-6 and the random number is 1-100, that means 6 out of 100 times (on average) it'll match. That's 6% of the time, not 5%.
But, what saves you here is that array_search returns the element of the the array that matched, which in the case of the first element is 0. The if statement is therefore false. If this is by design it seems to be quite sloppy coding.
So in the above example only the numbers 2-6 can match, so it would be correct at 5%. This won't work for case where you specify 100% though, because 1% of the time it'd still be false.
I therefore take back the statement about being off by one, but it's a subtle point, and easily overlooked.