모든 이야기

subnormal: 가장 작은 정상값 아래의 차이를 0으로 없애도 될까

서로 거의 같은 작은 두 수를 빼면, 차이는 두 입력보다 훨씬 작아진다. 이 차이가 정상 부동소수점 범위보다 작다는 이유로 곧바로 0이 되어도 될까. subnormal은 가장 작은 정상값 아래에서도 일정한 절대 간격으로 0까지 이어지는 작은 수의 표현이다.

큰 수부터 작은 수까지 같은 상대 정밀도로 표현한다는 정상값의 규칙을 끝없이 유지할 수는 없다. 지수의 범위가 유한하기 때문이다. subnormal은 그 끝에서 상대 정밀도를 줄이며 값의 연속성을 남긴다.

binary32의 가장 작은 구간을 확대하기

binary32의 유효숫자 정밀도는 정상값에서 24비트이고, 가장 작은 양의 정상값은 2⁻¹²⁶이다. subnormal의 최소 양수와 간격은 2⁻¹⁴⁹이다. 가장 큰 subnormal은 2⁻¹²⁶−2⁻¹⁴⁹이다.

두 정확한 입력 A=2⁻¹²⁶과 B=2⁻¹²⁶−2⁻¹⁴⁹를 빼면 결과는 2⁻¹⁴⁹다. 둘의 차이가 작지만 binary32에 정확히 존재한다. 결과를 무조건 0으로 만들면 서로 다른 두 입력의 차이를 잃는다.

정상값에서는 지수가 작아질 때 간격도 줄어든다. subnormal 구간에서는 간격이 2⁻¹⁴⁹로 고정된다. 값이 2⁻¹⁴⁹이면 다음 단계의 크기 변화가 그 값과 비슷한 수준이므로 상대 정밀도는 크게 낮아진다. 모든 작은 수에서 정상값과 같은 상대 오차를 보장한다는 뜻은 아니다.

HardFloat의 표현 형식 설명은 정상값과 subnormal을 구분하고, 내부 recoded 형식에서는 subnormal을 정규화해 연산에 사용하는 방식을 설명한다. 내부 표현을 바꾸어도 외부에서 표현할 수 있는 숫자의 집합을 바꾸지 않는 것이 이 recoding의 목적이다.

작은 결과를 0으로 바꾸는 정책

flush-to-zero 정책은 subnormal 결과를 0으로 처리할 수 있다. 위의 빼기 예에서 정확한 결과 2⁻¹⁴⁹가 0이 된다면, 두 입력이 같다는 것과 작은 차이를 표현하지 않는다는 것을 구분하기 어려워진다.

이런 정책은 구현의 부담이나 실행 경로를 줄이려는 선택일 수 있지만 수치 의미가 달라진다. 성능 이득이 모든 칩과 작업 부하에서 같다는 뜻은 아니다. 실제 지원 모드와 측정 조건을 확인해야 한다. subnormal이 느리다는 일반적인 표현만으로 특정 가속기의 동작을 판단할 수 없다.

또 작은 입력을 0으로 취급하는 정책과 작은 출력을 0으로 만드는 정책은 다르다. 입력 2⁻¹⁴⁹에 정상값 2¹⁰⁰을 곱하면 정확한 결과는 2⁻⁴⁹로, 정상 범위에 있다. 입력을 미리 0으로 취급했다면 결과는 0이다. 출력 범위만 검사해서는 이 차이를 알 수 없다.

따라서 비교할 때는 입력 처리, 중간 연산, 출력 처리와 exception flag의 규칙을 각각 적어야 한다. “subnormal 미지원”이라는 한 문구만으로 세부 계약이 충분히 드러나지 않을 수 있다.

작은 값과 underflow flag가 같은 말은 아니다

subnormal 결과가 나왔다고 항상 underflow flag가 설정되는 것은 아니다. IEEE 방식의 일반적인 underflow 판정에는 작은 결과와 inexact 조건이 함께 관련된다. 앞의 A−B는 정확히 표현되는 subnormal이므로, 그 사실만으로 underflow를 선언하면 계약이 달라질 수 있다.

RISC-V F 명세는 subnormal 연산을 IEEE 방식으로 처리하고, tininess를 반올림 뒤에 판단하도록 정의한다. HardFloat 제어 입력 설명은 tininess 검출 시점과 inexact에 관련된 underflow 의미를 다룬다. 목표 명령이나 모듈에서 어떤 규칙을 적용하는지 확인해야 한다.

아주 작은 정확한 결과, 아주 작은 부정확한 결과, 반올림으로 가장 작은 정상값이 되는 결과는 검증상 서로 다른 사건이다. 숫자 출력만 같은 테스트에서는 flag 차이를 놓칠 수 있다.

회로 내부에서 숨은 첫 1을 다시 만들기

정상값은 저장되지 않은 첫 유효 1을 가정할 수 있지만, subnormal은 그 위치가 달라진다. 입력을 공통 연산 경로로 보내려면 leading zero 수를 찾고 유효숫자와 지수를 함께 조정할 수 있다. HardFloat의 recoding이 이런 처리를 내부 표현에 포함하는 사례다.

이 과정에서 0 입력은 별도 상태다. 0에는 정규화할 첫 1이 없다. subnormal 변환과 zero 검사를 섞으면 지수 보정이나 특별값 판정이 잘못될 수 있다. 결과를 외부 형식으로 돌려보낼 때도 정상·subnormal 경계에서 필요한 반올림과 비트 위치를 지켜야 한다.

검증 입력은 최소 subnormal, 최대 subnormal, 최소 정상값과 그 이웃부터 잡으면 좋다. 정확한 상쇄 결과와 subnormal 입력이 정상 결과로 커지는 예를 함께 사용하면 입력과 출력의 0 처리 정책을 구분할 수 있다. 실제 모드를 고정하고 같은 reference 규칙을 사용해야 한다.

subnormal은 0 직전의 작은 수를 완전한 정밀도로 무한히 이어 주는 기술은 아니다. 정상 범위 아래에서 상대 정밀도를 지불하며 작은 차이를 남기는 표현이다. 그 값을 0으로 바꾸는 선택을 이해하려면 성능 주장보다 먼저, 어떤 입력과 결과가 달라지고 어떤 flag가 남는지를 보아야 한다.

subnormal: 가장 작은 정상값 아래의 차이를 0으로 없애도 될까 · ariser