I think you can only rely on the order of a table if you use integer keys (i.e. 1,2,3 … n). In your example the keys are in 0.5 intervals, and so the order is no longer reliable. You will notice that if you drop 0.5 from your iterator, your table will be in the expected order.
In summary, use integer keys if you want to rely on the order of your table. Otherwise the order can vary from one time to another.
If you know the step is in 0.5 interval then you can just make an integer based table and whenever you access it you multiply the key by 0.5. So 20 would be 10 (20 x 0.5) and 21 would be 10.5 (21 x 0.5). That seems kind of messy to me though.
You could design it such that the keys of the table are meaningless integers, and the meaningful values are stored in a table inside each value:
test[1] = {key = 10, value = 50}
test[2] = {key = 10.5, value = 55}
There may be better ways someone else might suggest. It really depends on what you are trying to achieve.
The “problem” is that ipairs() assumes a starting index of 1 with the index incrementing by 1 for each iteration and no gaps in the sequence (ie no nil values)