3. 차이는 언제 의미를 갖는가
0.1 + 0.2 == 0.3을 실행해 본 적이 있다면 결과가 false로 나오는 순간을 기억할 것이다. 처음 이 결과를 봤을 때는 계산기가 고장 난 줄 알았다. 손으로 풀어도 0.3이 나오는 계산인데, 컴퓨터는 어째서 다르다고 말하는 걸까. 답은 0.1과 0.2가 2진수 부동소수점으로는 정확히 표현되지 않는다는 데 있다. 두 값을 더한 결과는 0.3에 아주 가까운 근사치일 뿐, 완전히 같은 비트 배열은 아니다.
앞 장에서 우리는 같음의 기준을 값으로 정했다. 메모리 주소가 아니라 담긴 값이 같으면 같다고 부르기로 한 것이다. 그런데 부동소수점 앞에서는 그 기준마저 흔들린다. 값을 기준으로 삼겠다고 정했지만, 그 값이 어느 정도까지 일치해야 같다고 부를 것인지는 또 다른 결정이 필요하다. 그래서 부동소수점을 다루는 코드는 대개 == 대신 오차 허용 범위, 흔히 엡실론이라 부르는 작은 수를 두고 그 안에 들어오면 같다고 판정한다. 같음의 기준을 정하는 일과, 그 기준의 해상도를 정하는 일은 서로 다른 결정이다.
그런데 어떤 값은 해상도를 아무리 넉넉하게 잡아도 같아지지 않는다. IEEE 754 부동소수점 표준에서 NaN == NaN은 항상 false다. 2장에서 정리한 동치관계의 첫째 조건, 모든 대상은 자기 자신과 같다는 규칙을 정면으로 위반하는 값이 표준 안에 버젓이 들어 있는 셈이다. 이것은 결함이 아니라 의도된 선택이다. NaN은 0을 0으로 나눈 값처럼 정의되지 않은 연산의 결과를 표현하는데, 서로 다른 연산에서 나온 두 NaN이 같은 의미를 가진다고 보장할 수 없기 때문에 아예 같지 않다고 선언해 버린 것이다. 반사성이라는, 흔들릴 리 없어 보이던 규칙조차 특정 문맥에서는 무의미하거나 해로울 수 있다.
이 두 사례를 나란히 보면 차이가 의미를 갖는 시점이 조금 더 또렷해진다. 부동소수점 오차는 계산 과정에서 생긴 흔들림일 뿐 우리가 비교하려는 대상의 본질과는 무관하므로 무시해도 된다. 반면 NaN과 NaN 사이의 차이는 무시하면 안 된다. 겉보기에 같아 보이는 두 값이 실제로는 서로 무관한 두 개의 실패를 대표하고 있을 수 있기 때문이다. 결국 차이가 의미를 갖는지 여부는 값 자체가 아니라, 그 값이 어떤 문맥에서 무엇을 대표하는지에 달려 있다.
그렇다면 2장에서 던진 질문은 이렇게 다시 쓸 수 있다. 우리는 같음의 기준만 고르는 것이 아니라, 그 기준이 얼마나 엄격해야 하는지, 어떤 예외를 허용해야 하는지까지 함께 결정하고 있다. 이 결정은 결국 그 대상에 이름을 붙이는 행위와 맞닿아 있다. 같다고 판단한 것들을 하나의 이름 아래 묶는 순간, 그 이름은 무엇을 대표하게 되는 걸까. 다음 화에서는 이름을 붙인다는 행위 자체를 들여다본다.