부동소수점

부동소수점 (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의 원문에서 최신 내용을 확인할 수 있어요.