Solidity can't do floating point. This is why we store and transact with the smallest unit available by your decimals. They are the only unit that actually exists, ie: Wei in Ether. "1 Ether" is a fantasy. It simply means 1000000000000000000 (or 1e18) Wei.
Ether tokens have 18 decimals. Let's say we use 6 decimals instead and you want to buy ten tokens, at a value of 1 ERC20 per 0.01 ETH.
uint public decimals = 6
;
Because of this, our multiplier is 1e6, or 1000000. Six decimals. Ethereum's would be 1e18.
uint MULTIPLIER = 1000000; // six decimals to the left
uint price = 10000000000000000; // 1e16 wei per token
;
If you send 0.1 ETH, you're sending 1e17. The price of a token is 1e16.
uint amount = msg.value; // msg.value is 1e17
uint tokens = amount / price * MULTIPLIER; // or 1e17 / 1e16 * 1e6
// amount / price will be 10. then 10 * 1000000 gets you 10,000,000, a.k.a Ten of your ERC20 tokens valued at 1,000,000 "erc20 wei" each.
Tokens is the amount of tokens, to six decimals, you would use in token.transfer(address to, uint amount)
method as the amount.
Whenever you display this to the end user, you need to divide the result by the multiplier to get the actual amount of tokens, because we are storing in ERC20 "wei".
If you add the token to Metamask or visit it on Etherscan, it will be able to pull the "decimals" variable and do the math to display it accurately. This is how all Ethereum tokens display the correct value. The wallets are smart and know the ERC20 standard and look for a variable called decimals.
A lot of this is pseudocode and I'm sure the math is breakable, but the general idea is there. You might want to check that the minimum bid is 1e16 before processing, otherwise you'd end up with floating point errors ( ie, if someone sent in 1e15 wei and you divided it by 1e16).
edit: As well, where you say Because msg.value is in wei, sending 0.01 ETH, would result in receiving 1e18 tokens
is incorrect, 1 ETH = 1e18 wei. 0.01 eth would result in 1e16 wei.