What is happening here is pattern matching. Pattern matching happens on the form of an expression. But you need to be careful. What you see in a notebook is a visualization of the expression, a presentation form of the expression. This is an interface thing so that it looks reasonable to the human user. Pattern matching happens on the "real" form of the expression. You can see this "real" form by using FullForm (well, even this is probably not always the real form, but that's beyond my knowledge).
FullForm[0.5]
(*gives 0.5`*)
FullForm[1/2]
(*gives Rational[1, 2]*)
Since we're pattern matching, we would start by thinking about MatchQ.
MatchQ[0., 0] (*False*)
So, at the very least you can confirm for yourself that the expression 0. does not match the pattern 0. (<- that's the period of the sentence, not a numeric dot) And yes, since we're pattern matching, 0 is a pattern here. It's a pattern that can only match exactly one thing, since there are no possible variations (as there would be with _Integer for example). But saying "can only match exactly one thing" is effectively saying SameQ. Now, for what it's worth, I don't actually know if SameQ is used during pattern matching (it could be using some lower level function). So, for your question "how do you know /. looks for match using SameQ?", I admit I don't really know. But it's the semantics of pattern matching that you should be trying to understand. The reason why MatchQ[0., 0] is False is the same reason why MatchQ[0.5, 0] is False, and it's the same reason why MatchQ["0", 0] is False. The fact that 0. and 0 are somehow numerically close is just totally irrelevant.