21 million was not chosen, 21 million is the result of multiple other choices.
Initial block reward was 50BTC
1 block is made every 10 minutes
Block reward halves every 4 years
Those three choices lead to 21 million coins, after so many halvings the block reward eventually hits 0 and at that time the number of coins generated happens to be just under 21 million.
if you look into the source code, 21m was hardcoded, not derived as you have mentioned
Looks derived exactly as explained to me (except that the reward halves every 210000 blocks which is
approximately every 4 years):
https://github.com/bitcoin/bitcoin/blob/59310f1c02673c3ee068cd82f8654bed9b757889/src/main.cpp#L1229CAmount GetBlockValue(int nHeight, const CAmount& nFees)
{
CAmount nSubsidy = 50 * COIN;
int halvings = nHeight / Params().SubsidyHalvingInterval();
// Force block reward to zero when right shift is undefined.
if (halvings >= 64)
return nFees;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy >>= halvings;
return nSubsidy + nFees;
}