Your results are reasonable.
The two functions are designed for different purposes and they are based on completely different class of pseudorandom number generators.
rand()
is a pseudorandom number generator (PRNG), generally implemented as a linear congruential generator (LCG), which is reasonably fast but generally it has notoriously bad statistical properties.
arc4random()
is a cryptographically secure pseudorandom number generator (CSPRNG), which is generally slower but also suitable for cryptographical use, which implies it has higher statistical quality of randomness than a PRNG. According to the documentations here, here and here:
The function when originally implemented in OpenBSD, used the RC4
cipher to produce the output stream. OpenBSD now uses a ChaCha20
stream cipher.
and
RC4 has been designed by RSA Data Security, Inc. It was posted anonymously
to the USENET and was confirmed to be equivalent by several
sources who had access to the original cipher. Since RC4 used to be a
trade secret, the cipher is now referred to as ARC4.
It is important to understand what is the difference between the two class of pseudorandom number generators.
A PRNG is designed for purposes where all you care about is (ideally) high amount of statistical randomness: fields where you want simulate randomness.
A CSPRNG is designed for purposes where the latter is simply not enough, but high amount of unpredictability (even when the generator algorithm is exactly known) is also required: the field of cryptography.
So you don't need a CSPRNG to generate numbers with high quality of statistical randomness. All you need is a high quality PRNG. rand()
is nowhere to that and arc4random()
is an overshoot.
See this page for simple, modern, high quality random number generators.