부동소수점
부동소수점 (Floating-Point)
D의 부동소수점 중간값 정밀도, 컴파일 타임 상수 폴딩, 반올림 제어, 예외 플래그, 그리고 컴파일러가 허용·금지하는 부동소수점 변환 규칙을 설명하는 페이지예요. IEEE-754 규칙을 따르는 것이 핵심이에요.
출처: Floating-Point
본문
Floating-Point Intermediate Values
부동소수점 연산과 표현식 중간값에는 표현식의 타입보다 더 큰 정밀도를 사용할 수 있어요. 피연산자 타입이 정하는 것은 최소 정밀도일 뿐, 최대 정밀도는 아니에요. 구현 참고: 예를 들어 Intel x86 머신에서는 중간 계산이 하드웨어가 구현하는 80비트 전체 정밀도로 수행될 것으로 기대되지만(필수는 아님) 그래요.
부동소수점 표현식의 실행은 소스가 지시하는 것보다 더 큰 정밀도의 결과를 낼 수 있어요.
Floating-Point Constant Folding
피연산자의 타입과 무관하게 부동소수점 상수 폴딩은 real 이상의 정밀도로 수행돼요. 항상 IEEE-754 규칙을 따르며 round-to-nearest가 사용돼요.
부동소수점 상수는 상수의 타입과 무관하게 구현 내부에서 최소한 real 정밀도로 표현돼요. 추가 정밀도는 상수 폴딩에 사용할 수 있어요. 결과의 정밀도에 확정하는 작업은 컴파일 과정에서 가능한 한 늦게 수행돼요. 예를 들어:
const float f = 0.2f;
writeln(f - 0.2);
위는 0을 출력해요. 비-const 정적 변수의 값은 컴파일 타임에 전파될 수 없으므로:
static float f = 0.2f;
writeln(f - 0.2);
위는 2.98023e-09를 출력해요. 반올림의 영향을 받지 않는 특정 부동소수점 비트 패턴이 필요할 때는 16진수 부동소수점 상수를 쓸 수도 있어요. 0.2f의 16진수 값을 찾으려면:
import std.stdio;
void main()
{
writefln("%a", 0.2f);
}
그것은 0x1.99999ap-3이에요. 16진수 상수를 사용하면:
const float f = 0x1.99999ap-3f;
writeln(f - 0.2);
2.98023e-09를 출력해요.
서로 다른 컴파일러 설정, 최적화 설정, 인라이닝 설정은 상수 폴딩 기회에 영향을 줄 수 있으므로, 부동소수점 계산 결과는 그 설정들에 따라 달라질 수 있어요.
Rounding Control
IEEE 754 부동소수점 산술은 4가지 서로 다른 반올림 모드를 설정할 수 있는 기능을 포함해요. 이것들은 core.stdc.fenv의 함수들을 통해 접근할 수 있어요.
함수 안에서 부동소수점 반올림 모드를 바꾸면 함수가 종료되기 전에 복원해야 해요. 이 규칙을 위반하면(예를 들어 인라인 어셈블리 사용으로), 이후 계산에 사용되는 반올림 모드는 정의되지 않아요.
Exception Flags
IEEE 754 부동소수점 산술은 계산에서 무슨 일이 있었는지에 따라 몇 가지 플래그를 설정할 수 있어요:
| FE_INVALID |
|---|
| FE_DENORMAL |
| FE_DIVBYZERO |
| FE_OVERFLOW |
| FE_UNDERFLOW |
| FE_INEXACT |
이 플래그들은 core.stdc.fenv의 함수들을 통해 설정/재설정할 수 있어요.
Floating-Point Transformations
구현은 그 강도를 줄이기 위해 부동소수점 계산에 변환을 수행할 수 있어요.
모든 변환이 유효한 것은 아니에요: 아래 부동소수점 표현식 변환은 IEEE 규칙 아래에서 다른 결과를 만들 수 있기 때문에 허용되지 않아요.
허용되지 않는 부동소수점 변환
| transformation | comments |
|---|---|
| x + 0 → x | not valid if x is -0 |
| x - 0 → x | not valid if x is ±0 and rounding is towards -∞ |
| -x ↔ 0 - x | not valid if x is +0 |
| x - x → 0 | not valid if x is NaN or ±∞ |
| x - y ↔ -(y - x) | not valid because (1-1=+0) whereas -(1-1)=-0 |
| x * 0 → 0 | not valid if x is NaN or ±∞ |
| x / c ↔ x * (1/c) | valid if (1/c) yields an exact result |
| x != x → false | not valid if x is a NaN |
| x == x → true | not valid if x is a NaN |
| x !op y ↔ !(x op y) | not valid if x or y is a NaN |
물론, 부작용을 바꾸는 변환도 역시 유효하지 않아요.
더 알아보기
- 반올림 모드와 예외 플래그를 조작하는 함수는 core.stdc.fenv 문서를 참고하세요.
- IEEE 754 표준의 자세한 내용은 IEEE 754-2019를 참고하세요.
- dlang.org의 원문에서 최신 내용을 확인할 수 있어요.