Bfloat16 부동소수점

**bfloat16**(brain floating point) 부동소수점 형식은 주기억장치에서 16비트를 차지하는 컴퓨터 숫자 형식이다. 이는 부동 소수점(floating radix point)을 사용하여 넓은 동적 범위의 숫자 값을 나타낸다. 이 형식은 기계 학습 및 니어 센서 컴퓨팅의 가속화를 목표로 하는 32비트 IEEE 754 단정밀도 부동소수점 형식(binary32)의 단축(16비트) 버전이다. 8개의 지수 비트를 유지함으로써 32비트 부동소수점 숫자의 대략적인 동적 범위를 보존하지만, binary32 형식의 24비트 유효숫자 대신 8비트 정밀도만을 지원한다. 단정밀도 32비트 부동소수점 숫자보다 bfloat16 숫자는 정수 계산에는 부적합하지만, 이는 의도된 용도가 아니다. bfloat16은 기계 학습 알고리즘의 저장 요구 사항을 줄이고 계산 속도를 높이는 데 사용된다.

bfloat16 형식은 구글의 인공지능 연구 그룹인 구글 브레인에서 개발되었다. 이는 인텔 제온 프로세서(AVX-512 BF16 확장), 인텔 데이터 센터 GPU, 인텔 너바나 NNP-L1000, 인텔 FPGA, AMD 젠, AMD 인스팅트, 엔비디아 GPU, 구글 클라우드 TPU, AWS 인퍼렌티아, AWS 트레이니움, ARMv8.6-A, 애플의 M2 및 이후 A15 칩 등 다양한 CPU, GPU, AI 프로세서에서 활용된다. CUDA, 인텔 oneAPI Math Kernel Library, AMD ROCm, AMD 최적화 CPU 라이브러리, PyTorch, 텐서플로 등 여러 라이브러리가 bfloat16을 지원한다. 이러한 플랫폼에서 bfloat16은 혼합 정밀도 연산에서도 사용될 수 있으며, 이때 bfloat16 숫자는 더 넓은 자료형으로 연산되고 확장될 수 있다.

## bfloat16 부동소수점 형식

**bfloat16**은 다음 형식을 가진다.
* 부호 비트: 1 비트
* 지수 너비: 8 비트
* 유효숫자 정밀도: 8비트 (명시적으로 7비트 저장, 암시적 선행 비트 포함), 기존 단정밀도 부동소수점 형식의 24비트와는 대조적이다.

bfloat16 형식은 단축된 IEEE 754 단정밀도 32비트 부동소수점 형식이므로 IEEE 754 단정밀도 32비트 부동소수점 형식으로의 형 변환 및 그 반대 변환이 빠르다. bfloat16 형식으로 변환할 때 지수 비트는 보존되는 반면, 유효숫자 필드는 절단(따라서 0으로 반올림에 해당) 또는 기타 반올림 메커니즘에 의해 축소될 수 있으며, NaN 특수 사례는 무시된다. 지수 비트를 보존함으로써 32비트 부동소수점의 범위인 약 10−38에서 약 3 × 1038이 유지된다.

비트는 다음과 같이 배열된다.

### 지수 인코딩
bfloat16 이진 부동소수점 지수는 오프셋 이진 표현을 사용하여 인코딩되며, 0 오프셋은 127이다. 이는 IEEE 754 표준에서 지수 편향으로도 알려져 있다.
* Emin = 01H−7FH = −126
* Emax = FEH−7FH = 127
* 지수 편향 = 7FH = 127

따라서, 오프셋 이진 표현으로 정의된 실제 지수를 얻기 위해서는 지수 필드 값에서 127의 오프셋을 빼야 한다.

지수 필드의 최소 및 최대 값(00H 및 FFH)은 IEEE 754 표준 형식과 마찬가지로 특별하게 해석된다.

최소 양수 정규 값은 2−126 ≈ 1.18 × 10−38이고 최소 양수 (비정규) 값은 2−126−7 = 2−133 ≈ 9.2 × 10−41이다.

### 반올림 및 변환

가장 일반적인 사용 사례는 IEEE 754 binary32와 bfloat16 사이의 변환이다. 다음 섹션에서는 변환 과정과 변환 시의 반올림 방식을 설명한다. bfloat16으로 또는 bfloat16에서 다른 형식으로의 변환 시나리오도 있을 수 있음을 참고한다. 예를 들어, int16과 bfloat16.

* binary32에서 bfloat16으로. bfloat16이 처음 저장 형식으로 도입되었을 때, IEEE 754 binary32(32비트 부동소수점)에서 bfloat16으로의 변환은 절단(0으로 반올림)이었다. 이후 행렬 곱셈 단위의 입력이 되면서, 변환은 하드웨어 플랫폼에 따라 다양한 반올림 메커니즘을 가질 수 있다. 예를 들어, 구글 TPU의 경우 변환 시 반올림 방식은 가장 가까운 짝수로 반올림이다. ARM은 비IEEE Round-to-Odd 모드를 사용한다. 엔비디아의 경우, float 숫자를 가장 가까운 짝수로 반올림 모드로 bfloat16 정밀도로 변환하는 것을 지원한다.
* bfloat16에서 binary32로. binary32는 bfloat16의 모든 정확한 값을 표현할 수 있으므로, 변환은 단순히 유효숫자 비트에 16개의 0을 채운다.

## 특수 값 인코딩

### 양의 무한대와 음의 무한대
IEEE 754에서와 마찬가지로, 양의 무한대와 음의 무한대는 해당 부호 비트와 8개의 지수 비트가 모두 설정(FFhex)되고 모든 유효숫자 비트가 0으로 표현된다. 명시적으로,
```text
val    s_exponent_signcnd
+inf = 0_11111111_0000000
-inf = 1_11111111_0000000
```


### 숫자가 아님
IEEE 754에서와 마찬가지로, NaN 값은 부호 비트, 8개의 지수 비트가 모두 설정(FFhex)되고 모든 유효숫자 비트가 0이 아닌 경우로 표현된다. 명시적으로,
```text
val    s_exponent_signcnd
+NaN = 0_11111111_klmnopq
-NaN = 1_11111111_klmnopq
```
여기서 k, l, m, n, o, p, q 중 적어도 하나는 1이다. IEEE 754와 마찬가지로, NaN 값은 quiet 또는 signaling일 수 있지만, 2018년 9월 현재 signaling bfloat16 NaN의 알려진 사용 사례는 없다.

## 범위 및 정밀도

Bfloat16은 32비트 IEEE 754 단정밀도 부동소수점 형식(binary32)의 숫자 범위를 유지하면서 정밀도를 24비트에서 8비트로 줄이도록 설계되었다. 이는 정밀도가 2에서 3십진수 자릿수 사이이며, bfloat16은 최대 약 3.4 × 1038의 유한 값을 표현할 수 있음을 의미한다.

## 예시

이 예시들은 부동소수점 값의 비트 표현을 십육진법과 이진법으로 보여준다. 여기에는 부호, (편향된) 지수, 유효숫자가 포함된다.
 3f80 = 0 01111111 0000000 = 1
 c000 = 1 10000000 0000000 = −2

 7f7f = 0 11111110 1111111 = (28 − 1) × 2−7 × 2127 ≈ 3.38953139 × 1038 (bfloat16 정밀도에서 최대 유한 양수 값)
 0080 = 0 00000001 0000000 = 2−126 ≈ 1.175494351 × 10−38 (bfloat16 정밀도 및 단정밀도 부동소수점에서 최소 정규 양수 값)
일반 bfloat16 숫자의 최대 양의 유한 값은 3.38953139 × 1038이며, 단정밀도에서 표현 가능한 최대 양의 유한 값인 (224 − 1) × 2−23 × 2127 = 3.402823466 × 1038보다 약간 낮다.

### 영과 무한대
 0000 = 0 00000000 0000000 = 0
 8000 = 1 00000000 0000000 = −0

 7f80 = 0 11111111 0000000 = 무한대
 ff80 = 1 11111111 0000000 = −무한대

### 특수 값
 4049 = 0 10000000 1001001 = 3.140625 ≈ π ( 파이 )
 3eab = 0 01111101 0101011 = 0.333984375 ≈ 1/3

### NaNs
 ffc1 = x 11111111 1000001 => qNaN
 ff81 = x 11111111 0000001 => sNaN

## 같이 보기
* 반정밀도 부동소수점수: IEEE 754에 의해 정의된 1비트 부호, 5비트 지수, 11비트 유효숫자를 가진 16비트 부동소수점
* ISO/IEC 10967, 언어 독립 산술
* 원시 자료형
* 미니플로트
* 구글 브레인
* TPU에서 bfloat16을 사용한 구글에 대한 소송

## 각주

분류:이진 산술