비정규 부동 소수점 숫자는 무엇입니까?


82

isnormal () 참조 페이지 는 다음을 알려줍니다.

주어진 부동 소수점 수 arg가 정상인지, 즉 0, 비정규, 무한 또는 NaN이 아닌지 판별합니다.

0, 무한 또는 NaN 인 숫자는 그 의미가 분명합니다. 그러나 그것은 또한 비정상이라고 말합니다. 숫자는 언제 비정규입니까?


2
첫 번째 Google 결과는 비정규 어의 동의어 일뿐임을 보여줍니다. en.wikipedia.org/wiki/Denormal_number
tenfour

10
그러나 이제 Google에서 두 번째 히트 (“비정상 부동 소수점”검색) 는이 질문 그 자체입니다.
Slipp D. Thompson 2013

비정규 항목에 대한 심도있는 토론과 처리 방법은 다음 질문을 참조하십시오. stackoverflow.com/questions/9314534/…
fig

답변:


79

IEEE754 표준에서 부동 소수점 숫자는 이진 과학 표기법 x  =  M  × 2 e로 표시 됩니다. 여기서 M가수 이고 e지수 입니다. 수학적으로, 당신은 항상 지수를 선택할 수 1 ≤  M  <2 * 그러나, 컴퓨터 표현에 지수는 한정된 범위를 가질 수 있기 때문에,이 0보다 더 큰 몇 가지 숫자가 있지만보다 작은 1.0 × 2 전자 min . 그 숫자는 비정규 또는 비정규 입니다.

실제로 가수는 비정규 숫자 (및 0)를 제외하고 항상 선행 1이 있기 때문에 선행 1없이 저장됩니다 . 따라서 해석은 지수가 최소가 아니면 암시 적 선행 1이 있고 지수가 최소이면 존재하지 않으며 숫자가 비정규라는 것입니다.

*)보다 일반적으로,   모든 base- B 과학적 표기법에 대해 1 ≤  M  <  B 입니다 .


당신이 말하는이 isnomal입니다 true8 개 비트가 모두 0과 경우에 false, 그렇지?
Pacerier

'저장'또는 해석?
Pacerier

@Pacerier : "저장"그것은 예로서, 최고의 1없이 저장된 001010해석 으로 1.001010.
Kerrek SB

'e <sub> min </ sub>에서 언급 된 emin이 무엇인지 분명합니까? ```(내 포맷 시도가 효과가 있기를 바랍니다.) ..
Razzle

85

IEEE 754 기본 사항

먼저 IEEE 754 번호의 기본 구성을 살펴 보겠습니다.

단 정밀도 (32 비트)에 중점을 둘 것이지만 모든 것이 즉시 다른 정밀도로 일반화 될 수 있습니다.

형식은 다음과 같습니다.

  • 1 비트 : 부호
  • 8 비트 : 지수
  • 23 비트 : 분수

또는 사진이 마음에 들면 :

여기에 이미지 설명 입력

소스 .

기호는 간단합니다. 0은 양수, 1은 음수, 이야기의 끝입니다.

지수는 8 비트 길이이므로 범위는 0에서 255까지입니다.

지수는 오프셋이이므로 편향이라고합니다 -127. 예 :

  0 == special case: zero or subnormal, explained below
  1 == 2 ^ -126
    ...
125 == 2 ^ -2
126 == 2 ^ -1
127 == 2 ^  0
128 == 2 ^  1
129 == 2 ^  2
    ...
254 == 2 ^ 127
255 == special case: infinity and NaN

최고의 비트 규칙

(다음은 실제 역사적 연구를 기반으로하지 않은 가상의 가상 내러티브입니다.)

IEEE 754를 설계하는 동안 엔지니어들은를 제외한 모든 숫자 가 첫 번째 숫자로 2 진수 0.01 1을 가지고 있음을 발견했습니다 . 예 :

25.0   == (binary) 11001 == 1.1001 * 2^4
 0.625 == (binary) 0.101 == 1.01   * 2^-1

둘 다 그 성가신 1.부분으로 시작 합니다.

따라서 그 숫자가 거의 모든 단일 숫자를 하나의 정밀도 비트를 차지하도록하는 것은 낭비입니다.

이러한 이유로 그들은 "선도적 인 비트 규칙"을 만들었습니다.

항상 숫자가 1로 시작한다고 가정합니다.

하지만 어떻게 처리 0.0할까요? 글쎄, 그들은 예외를 만들기로 결정했습니다.

  • 지수가 0이면
  • 분수는 0
  • 숫자는 플러스 또는 마이너스를 나타냅니다. 0.0

그래서 바이트 00 00 00 00도를 나타내 므로 0.0좋아 보입니다.

이러한 규칙 만 고려하면 나타낼 수있는 가장 작은 0이 아닌 숫자는 다음과 같습니다.

  • 지수 : 0
  • 분수 : 1

선행 비트 규칙으로 인해 16 진수 분수에서 다음과 같이 보입니다.

1.000002 * 2 ^ (-127)

여기서,은 .000002A의 22 인 제로 1끝에.

우리는 받아 들일 수 없습니다 fraction = 0. 그렇지 않으면 그 숫자가 될 것입니다 0.0.

그러나 예리한 미적 감각을 가진 엔지니어들은 이렇게 생각했습니다. 우리가 직선 0.0에서 2의 적절한 거듭 제곱도 아닌 다른 것으로 점프 한다는 것은? 어떻게 든 더 작은 숫자를 나타낼 수는 없습니까? (좋아요, "추악한"것보다 조금 더 걱정 스러웠습니다. 실제로 사람들이 계산에 대해 나쁜 결과를 얻었습니다. 아래의 "비정규가 계산을 개선하는 방법"을 참조하십시오).

비정규 숫자

엔지니어들은 잠시 머리를 긁적이며 평소처럼 또 다른 좋은 아이디어를 가지고 돌아 왔습니다. 새 규칙을 만들면 어떻게 되나요?

지수가 0이면 :

  • 선행 비트는 0이됩니다.
  • 지수는 -126으로 고정됩니다 (이 예외가없는 것처럼 -127이 아님).

이러한 숫자를 비정규 숫자 (또는 동의어 인 비정규 숫자)라고합니다.

이 규칙은 즉시 다음과 같은 숫자를 의미합니다.

  • 지수 : 0
  • 분수 : 0

is still 0.0, 이는 추적해야 할 규칙이 하나 적다는 것을 의미하므로 우아합니다.

그래서 0.0우리의 정의에 따르면 실제로 비정규 숫자입니다!

이 새로운 규칙에서 가장 작은 비정규 숫자는 다음과 같습니다.

  • 지수 : 1 (0은 비정규 임)
  • 분수 : 0

이는 다음을 나타냅니다.

1.0 * 2 ^ (-126)

그런 다음 가장 큰 비정규 수는 다음과 같습니다.

  • 지수 : 0
  • 분수 : 0x7FFFFF (23 비트 1)

다음과 같습니다.

0.FFFFFE * 2 ^ (-126)

여기서 .FFFFFE다시 점의 우측에 23 비트이다.

이것은 정상이 아닌 가장 작은 수에 매우 가깝습니다.

그리고 가장 작은 0이 아닌 비정규 숫자는 다음과 같습니다.

  • 지수 : 0
  • 분수 : 1

다음과 같습니다.

0.000002 * 2 ^ (-126)

그것은 또한 꽤 가깝게 보입니다 0.0!

그보다 작은 숫자를 표현할 수있는 현명한 방법을 찾을 수 없었던 엔지니어들은 기뻐했고 다시 온라인으로 고양이 사진을 보거나 70 년대에했던 것이 무엇이든 봤습니다.

보시다시피 비정규 숫자는 정밀도와 표현 길이 사이에서 균형을 이룹니다.

가장 극단적 인 예로서, 가장 작은 0이 아닌 비정규 :

0.000002 * 2 ^ (-126)

기본적으로 32 비트 대신 단일 비트의 정밀도를가집니다. 예를 들어 2로 나누면 :

0.000002 * 2 ^ (-126) / 2

우리는 실제로 0.0정확히 도달 합니다!

심상

우리가 배우는 것에 대해 기하학적 인 직관을 갖는 것은 항상 좋은 생각입니다.

주어진 각 지수에 대해 IEEE 754 부동 소수점 숫자를 한 줄에 플로팅하면 다음과 같습니다.

          +---+-------+---------------+-------------------------------+
exponent  |126|  127  |      128      |              129              |
          +---+-------+---------------+-------------------------------+
          |   |       |               |                               |
          v   v       v               v                               v
          -------------------------------------------------------------
floats    ***** * * * *   *   *   *   *       *       *       *       *
          -------------------------------------------------------------
          ^   ^       ^               ^                               ^
          |   |       |               |                               |
          0.5 1.0     2.0             4.0                             8.0

그것으로부터 우리는 그것을 볼 수 있습니다 :

  • 각 지수에 대해 표시된 숫자간에 겹침이 없습니다.
  • 각 지수에 대해 동일한 숫자 2 ^ 32의 숫자 (여기서는 4로 *표시됨)가 있습니다.
  • 각 지수 내에서 점은 동일한 간격으로 배치됩니다.
  • 더 큰 지수는 더 큰 범위를 포함하지만 점이 더 분산되어 있습니다.

이제이를 지수 0까지 낮추겠습니다.

비정규가 없으면 가상적으로 다음과 같이 보일 것입니다.

          +---+---+-------+---------------+-------------------------------+
exponent  | ? | 0 |   1   |       2       |               3               |
          +---+---+-------+---------------+-------------------------------+
          |   |   |       |               |                               |
          v   v   v       v               v                               v
          -----------------------------------------------------------------
floats    *    **** * * * *   *   *   *   *       *       *       *       *
          -----------------------------------------------------------------
          ^   ^   ^       ^               ^                               ^
          |   |   |       |               |                               |
          0   |   2^-126  2^-125          2^-124                          2^-123
              |
              2^-127

비법 선의 경우 다음과 같습니다.

          +-------+-------+---------------+-------------------------------+
exponent  |   0   |   1   |       2       |               3               |
          +-------+-------+---------------+-------------------------------+
          |       |       |               |                               |
          v       v       v               v                               v
          -----------------------------------------------------------------
floats    * * * * * * * * *   *   *   *   *       *       *       *       *
          -----------------------------------------------------------------
          ^   ^   ^       ^               ^                               ^
          |   |   |       |               |                               |
          0   |   2^-126  2^-125          2^-124                          2^-123
              |
              2^-127

두 그래프를 비교하면 다음을 확인할 수 있습니다.

  • subnormals는 지수의 범위의 길이를 두 배로 0부터 [2^-127, 2^-126)[0, 2^-126)

    비정규 범위에서 부동 소수점 사이의 공간은 for와 동일합니다 [0, 2^-126).

  • 범위 [2^-127, 2^-126)에는 비정규가 없을 경우 점 수의 절반이 있습니다.

    이 포인트의 절반은 범위의 나머지 절반을 채우는 데 사용됩니다.

  • 범위 [0, 2^-127)에는 비정규가있는 점이 있지만없는 점은 없습니다.

    이 점의 부족은 [0, 2^-127)그다지 우아하지 않으며 비정규가 존재하는 주된 이유입니다!

  • 포인트 간격이 동일하기 때문에 :

    • 범위 [2^-128, 2^-127)는 포인트의 절반이 [2^-127, 2^-126) - [2^-129, 2^-128)포인트의 절반이[2^-128, 2^-127)
    • 등등

    이것은 비정규가 크기와 정밀도 사이의 균형이라고 말할 때 의미하는 것입니다.

실행 가능한 C 예제

이제 우리의 이론을 검증하기 위해 실제 코드를 가지고 놀자.

거의 모든 현재 및 데스크톱 컴퓨터에서 C float는 단 정밀도 IEEE 754 부동 소수점 숫자를 나타냅니다.

이것은 특히 내 Ubuntu 18.04 amd64 Lenovo P51 노트북의 경우입니다.

이러한 가정하에 모든 어설 션은 다음 프로그램을 전달합니다.

subnormal.c

#if __STDC_VERSION__ < 201112L
#error C11 required
#endif

#ifndef __STDC_IEC_559__
#error IEEE 754 not implemented
#endif

#include <assert.h>
#include <float.h> /* FLT_HAS_SUBNORM */
#include <inttypes.h>
#include <math.h> /* isnormal */
#include <stdlib.h>
#include <stdio.h>

#if FLT_HAS_SUBNORM != 1
#error float does not have subnormal numbers
#endif

typedef struct {
    uint32_t sign, exponent, fraction;
} Float32;

Float32 float32_from_float(float f) {
    uint32_t bytes;
    Float32 float32;
    bytes = *(uint32_t*)&f;
    float32.fraction = bytes & 0x007FFFFF;
    bytes >>= 23;
    float32.exponent = bytes & 0x000000FF;
    bytes >>= 8;
    float32.sign = bytes & 0x000000001;
    bytes >>= 1;
    return float32;
}

float float_from_bytes(
    uint32_t sign,
    uint32_t exponent,
    uint32_t fraction
) {
    uint32_t bytes;
    bytes = 0;
    bytes |= sign;
    bytes <<= 8;
    bytes |= exponent;
    bytes <<= 23;
    bytes |= fraction;
    return *(float*)&bytes;
}

int float32_equal(
    float f,
    uint32_t sign,
    uint32_t exponent,
    uint32_t fraction
) {
    Float32 float32;
    float32 = float32_from_float(f);
    return
        (float32.sign     == sign) &&
        (float32.exponent == exponent) &&
        (float32.fraction == fraction)
    ;
}

void float32_print(float f) {
    Float32 float32 = float32_from_float(f);
    printf(
        "%" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
        float32.sign, float32.exponent, float32.fraction
    );
}

int main(void) {
    /* Basic examples. */
    assert(float32_equal(0.5f, 0, 126, 0));
    assert(float32_equal(1.0f, 0, 127, 0));
    assert(float32_equal(2.0f, 0, 128, 0));
    assert(isnormal(0.5f));
    assert(isnormal(1.0f));
    assert(isnormal(2.0f));

    /* Quick review of C hex floating point literals. */
    assert(0.5f == 0x1.0p-1f);
    assert(1.0f == 0x1.0p0f);
    assert(2.0f == 0x1.0p1f);

    /* Sign bit. */
    assert(float32_equal(-0.5f, 1, 126, 0));
    assert(float32_equal(-1.0f, 1, 127, 0));
    assert(float32_equal(-2.0f, 1, 128, 0));
    assert(isnormal(-0.5f));
    assert(isnormal(-1.0f));
    assert(isnormal(-2.0f));

    /* The special case of 0.0 and -0.0. */
    assert(float32_equal( 0.0f, 0, 0, 0));
    assert(float32_equal(-0.0f, 1, 0, 0));
    assert(!isnormal( 0.0f));
    assert(!isnormal(-0.0f));
    assert(0.0f == -0.0f);

    /* ANSI C defines FLT_MIN as the smallest non-subnormal number. */
    assert(FLT_MIN == 0x1.0p-126f);
    assert(float32_equal(FLT_MIN, 0, 1, 0));
    assert(isnormal(FLT_MIN));

    /* The largest subnormal number. */
    float largest_subnormal = float_from_bytes(0, 0, 0x7FFFFF);
    assert(largest_subnormal == 0x0.FFFFFEp-126f);
    assert(largest_subnormal < FLT_MIN);
    assert(!isnormal(largest_subnormal));

    /* The smallest non-zero subnormal number. */
    float smallest_subnormal = float_from_bytes(0, 0, 1);
    assert(smallest_subnormal == 0x0.000002p-126f);
    assert(0.0f < smallest_subnormal);
    assert(!isnormal(smallest_subnormal));

    return EXIT_SUCCESS;
}

GitHub 업스트림 .

다음으로 컴파일 및 실행 :

gcc -ggdb3 -O0 -std=c11 -Wall -Wextra -Wpedantic -Werror -o subnormal.out subnormal.c
./subnormal.out

C ++

C의 모든 API를 노출하는 것 외에도 C ++는 C에서 쉽게 사용할 수없는 일부 추가 비정상 관련 기능을 노출합니다 <limits>. 예 :

  • denorm_min: T 유형의 최소 양수 비정규 값을 반환합니다.

C ++에서 전체 API는 각 부동 소수점 유형에 대해 템플릿이 지정되며 훨씬 더 좋습니다.

구현

x86_64 및 ARMv8은 C 코드가 변환하는 하드웨어에서 직접 IEEE 754를 구현합니다.

특정 구현에서 비정규는 노멀보다 느리게 보입니다. 0.1f를 0으로 변경하면 성능이 10 배 저하되는 이유는 무엇입니까? 이것은 ARM 매뉴얼에 언급되어 있습니다.이 답변의 "ARMv8 세부 정보"섹션을 참조하십시오.

ARMv8 세부 정보

ARM 아키텍처 참조 설명서 ARMv8 DDI 0487C.a 설명서 A1.5.4 "0으로 플러시"에서는 성능 향상을 위해 비정규를 0으로 반올림하는 구성 가능한 모드를 설명합니다.

비정규 화 된 숫자 및 언더 플로 예외와 관련된 계산을 수행 할 때 부동 소수점 처리 성능이 저하 될 수 있습니다. 많은 알고리즘에서 비정규 화 된 피연산자와 중간 결과를 0으로 대체하여 최종 결과의 정확도에 큰 영향을주지 않고이 성능을 복구 할 수 있습니다. 이 최적화를 허용하기 위해 ARM 부동 소수점 구현에서는 다음과 같이 다양한 부동 소수점 형식에 0으로 플러시 모드를 사용할 수 있습니다.

  • AArch64의 경우 :

    • 이면 FPCR.FZ==1모든 명령어의 모든 단 정밀도 및 배정 밀도 입력 및 출력에 0으로 플러시 모드가 사용됩니다.

    • 이면 FPCR.FZ16==1Flush-to-Zero 모드가 다음을 제외한 부동 소수점 명령어의 모든 반 정밀도 입력 및 출력에 사용됩니다.-반 정밀도와 단 정밀도 숫자 간 변환 번호.

A1.5.2 "부동 소수점 표준 및 용어"표 A1-3 "부동 소수점 용어"는 비정규 및 비정규가 동의어임을 확인합니다.

This manual                 IEEE 754-2008
-------------------------   -------------
[...]
Denormal, or denormalized   Subnormal

C5.2.7 "FPCR, 부동 소수점 제어 레지스터"는 ARMv8이 부동 소수점 연산의 입력이 비정상 일 때마다 선택적으로 예외를 발생 시키거나 플래그 비트를 설정하는 방법을 설명합니다.

FPCR.IDE, 비트 [15] 입력 비정상 부동 소수점 예외 트랩 활성화. 가능한 값은 다음과 같습니다.

  • 0b0 트랩되지 않은 예외 처리가 선택되었습니다. 부동 소수점 예외가 발생하면 FPSR.IDC 비트가 1로 설정됩니다.

  • 0b1 트랩 된 예외 처리가 선택되었습니다. 부동 소수점 예외가 발생하면 PE는 FPSR.IDC 비트를 업데이트하지 않습니다. 트랩 처리 소프트웨어는 FPSR.IDC 비트를 1로 설정할지 여부를 결정할 수 있습니다.

D12.2.88 "MVFR1_EL1, AArch32 미디어 및 VFP 기능 레지스터 1"은 비정상 지원이 실제로 완전히 선택 사항임을 보여 주며 지원이 있는지 감지 할 수있는 비트를 제공합니다.

FPFtZ, 비트 [3 : 0]

제로 모드로 플러시합니다. 부동 소수점 구현이 0으로 플러시 작동 모드 만 지원하는지 여부를 나타냅니다. 정의 된 값은 다음과 같습니다.

  • 0b0000 구현되지 않았거나 하드웨어가 Flush-to-Zero 작동 모드 만 지원합니다.

  • 0b0001 하드웨어는 완전한 비정규 화 된 숫자 산술을 지원합니다.

다른 모든 값은 예약되어 있습니다.

ARMv8-A에서 허용되는 값은 0b0000 및 0b0001입니다.

이것은 비정규가 구현되지 않은 경우 구현이 0으로 플러시로 되돌아 간다는 것을 의미합니다.

무한대와 NaN

궁금한? 다음에서 몇 가지를 작성했습니다.

비정규가 계산을 개선하는 방법

TODO : 점프가 계산 결과를 악화시키는 방법 / 비정규가 계산 결과를 개선하는 방법을 더 정확하게 이해합니다.

실제 역사

부동 소수점의 노인과의 인터뷰 에 의해 찰스 세브란스 . (1998)과의 인터뷰의 형태로 짧은 현실 세계 역사의 개요입니다 윌리엄 카한이 코멘트에 존 콜맨에 의해 제안되었다.


1
'IEEE 754 .. 디자인 중'표창? 또는 'Supposedly'로 문장을 시작하는 것이 더 좋습니다
Pacerier

@Pacerier 나는 그 사실이 틀릴 수 있다고 생각하지 않는다 :-) 그것에 대한 다른 근거가있을 수 있습니까? 이것은 이전에 알려졌을 것 같지만 괜찮다고 생각합니다.
치로 틸리는郝海东冠状病六四事件法轮功

멋진 대답입니다. 나는 봄에 수치 분석 수업을 가르 칠 준비를하고 있으며 학생들에게 이것으로 안내 할 것입니다 (우리의 텍스트는 간략한 토론이 있지만 세부 사항은 생략했습니다). 일부 결정에 대한 근거에 관해서는 다음과 같은 깨달음을 발견했습니다 . 부동 소수점의 노인과의 인터뷰 .
John Coleman

@JohnColeman이 링크에 감사드립니다! 당신을 인용하는 답변에 추가되었습니다. 누군가가 다른 대답에서 비정규가 계산 결과를 더 좋게 만드는 가능한 가장 짧고 의미있는 예를 추가 할 수 있다면 놀라 울 것입니다 (그리고 결과가 나빠지는 인공적인 예)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

29

에서 http://blogs.oracle.com/d/entry/subnormal_numbers :

예를 들어 10 진수를 사용하여 동일한 숫자를 나타내는 여러 가지 방법이 있습니다. 숫자 0.1은 1 * 10 -1 또는 0.1 * 10 0 또는 심지어 0.01 * 10 으로 나타낼 수 있습니다 . 표준은 숫자가 항상 첫 번째 비트. 1 * 10-1 예제에 해당하는 십진수로.

이제 표현할 수있는 가장 낮은 지수가 -100이라고 가정합니다. 따라서 일반 형식으로 표현할 수있는 가장 작은 숫자는 1 * 10 -100 입니다. 그러나 선행 비트가 1이라는 제약을 완화하면 실제로 동일한 공간에서 더 작은 숫자를 나타낼 수 있습니다. 소수의 예를 들면 0.1 * 10 -100을 나타낼 수 있습니다. 이것을 비정규 숫자라고합니다. 비정규 숫자를 갖는 목적은 가장 작은 정규 숫자와 0 사이의 간격을 부드럽게하는 것입니다.

비정규 숫자는 정규 숫자보다 정밀도가 낮다는 것을 인식하는 것이 매우 중요합니다. 사실, 그들은 더 작은 크기에 대해 감소 된 정밀도를 거래하고 있습니다. 따라서 비정규 숫자를 사용하는 계산은 일반 숫자에 대한 계산과 동일한 정밀도를 갖지 않습니다. 따라서 비정규 숫자에 대해 상당한 계산을 수행하는 응용 프로그램은 배율을 다시 조정 (즉, 숫자에 일부 배율 인수를 곱하는 것)이 비정규 수가 적고 더 정확한 결과를 얻을 수 있는지 조사 할 가치가있을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.