C++ 什么是次正常浮点数?

C++ 什么是次正常浮点数?,c++,c++11,floating-point,ieee-754,C++,C++11,Floating Point,Ieee 754,说明: 确定给定的浮点数arg是否正常,即 既不是零、次正常、无限,也不是NaN 一个数字是零、无限或NaN,很清楚它的意思。但它也表示低于正常水平。数字何时低于正常值?来自: 表示同一个数字可能有多种方式, 以十进制为例,数字0.1可以表示为 1*10-1或0.1*100,甚至0.01*10。标准规定 数字总是以第一位作为1存储。用十进制表示 对应于1×10-1示例 现在假设可以表示的最低指数是-100。 所以可以用正规形式表示的最小数是 1*10-100. 但是,如果我们放松前导位的约束 一

说明:

确定给定的浮点数arg是否正常,即 既不是零、次正常、无限,也不是NaN

一个数字是零、无限或NaN,很清楚它的意思。但它也表示低于正常水平。数字何时低于正常值?

来自:

表示同一个数字可能有多种方式, 以十进制为例,数字0.1可以表示为 1*10-1或0.1*100,甚至0.01*10。标准规定 数字总是以第一位作为1存储。用十进制表示 对应于1×10-1示例

现在假设可以表示的最低指数是-100。 所以可以用正规形式表示的最小数是 1*10-100. 但是,如果我们放松前导位的约束 一个一,那么我们实际上可以用同样的方法表示更小的数 空间以十进制为例,我们可以表示0.1*10-100。这 被称为次正常数。具有次正常数的目的 是平滑最小法向数和零之间的间隙

认识到代表次正常数字是非常重要的 精度低于正常数字。事实上,他们正在交易 由于尺寸较小,精度降低。因此,使用 低于正常值的数字将不会有与之相同的精度 正常数的计算。所以一个应用程序 对次正常数的重要计算可能是值得的 调查是否重新缩放(即将数字乘以 一些比例因子)将产生更少的次正常值,并且更精确 结果


在IEEE754标准中,浮点数表示为二进制科学符号,x=M×2e。这里M是尾数,e是指数。从数学上讲,您可以选择指数,使1≤MIEEE 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时,工程师们注意到,除了
0.0
,所有数字的第一位都是二进制的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
也表示
0.0
,看起来不错

如果我们只考虑这些规则,那么可以表示的最小非零数为:

  • 指数:0
  • 分数:1
由于前导位惯例,在十六进制分数中看起来像这样:

1.000002 * 2 ^ (-127)
其中,
.000002
为22个零,末尾为
1

我们不能取分数=0,否则这个数字将是
0.0

但是,同样具有敏锐审美意识的工程师们想:这不是很难看吗?我们从直线
0.0
跳到甚至不是2的适当幂的东西?难道我们不能以某种方式表示更小的数字吗?(好吧,这比“丑陋”更令人担忧:事实上,人们的计算结果不好,请参见下面的“低于正常值如何改进计算”)

低于正常值的数字

工程师们挠头了一会儿,像往常一样,带着另一个好主意回来了。如果我们创建一个新规则会怎么样:

如果指数为0,则:

  • 前导位变为0
  • 指数固定为-126(而不是-127,好像我们没有这个异常)
这样的数称为次正规数(或同义的非正规数)

该规则立即意味着该数字应满足以下条件:

  • 指数:0
  • 分数:0
仍然是
0.0
,这有点优雅,因为它意味着少了一条需要跟踪的规则

因此,根据我们的定义,
0.0
实际上是一个低于正常值的数字

根据这一新规则,最小的非次正常数为:

  • 指数:1(0将低于正常值)
  • 分数:0
它代表:

1.0 * 2 ^ (-126)
那么,最大的次正常数是:

  • 指数:0
  • 分数:0x7FFFFF(23位1)
这等于:

0.FFFFFE * 2 ^ (-126)
0.000002 * 2 ^ (-126)
其中,
.fffff e
再次是点右侧的23位1

这非常接近最小的非正常数字,听起来很正常

最小非零次正态数为:

  • 指数:0
  • 分数:1
这等于:

0.FFFFFE * 2 ^ (-126)
0.000002 * 2 ^ (-126)
它看起来也非常接近
0.0

由于找不到任何合理的方法来表示比这个数字小的数字,工程师们很高兴,于是又回到了在线查看猫的图片,或者他们在70年代所做的任何事情

正如你所看到的,低于正常值的数字在两者之间做了权衡
          +---+-------+---------------+-------------------------------+
exponent  |126|  127  |      128      |              129              |
          +---+-------+---------------+-------------------------------+
          |   |       |               |                               |
          v   v       v               v                               v
          -------------------------------------------------------------
floats    ***** * * * *   *   *   *   *       *       *       *       *
          -------------------------------------------------------------
          ^   ^       ^               ^                               ^
          |   |       |               |                               |
          0.5 1.0     2.0             4.0                             8.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
#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;
}
gcc -ggdb3 -O0 -std=c11 -Wall -Wextra -Wpedantic -Werror -o subnormal.out subnormal.c
./subnormal.out
This manual                 IEEE 754-2008
-------------------------   -------------
[...]
Denormal, or denormalized   Subnormal