关于c ++:什么是次正规浮点数?

What is a subnormal floating point number?

isnormal()引用页显示:

Determines if the given floating point number arg is normal, i.e. is
neither zero, subnormal, infinite, nor NaN.

一个零、无穷大或NaN的数字很清楚它的意思。但它也表示低于正常值。什么时候数字低于正常值?


在IEEE754标准的浮点数,二进制是为代表的科学记数法,X =  ; ; ; ;&时报;2e。这里是mantissa M和E是指数。mathematically选择指数,你可以总是这样1  ; ;&乐;M< ; ;2。然而,由于在计算机表示的指数只能有一个有限的范围内,有一些号码,这是大于零,但比1.0小 ;时报; ;2<><>/min的子子。这些数字是subnormals或denormals。

几乎没有mantissa是存储,因为总是有1领先,1领先,除了subnormal号码(零)。这样的解释是,该指数是在非最小的,有隐领先指数是1,如果不是最小的,有一个subnormal,and the number。

< > *)子公司1个,与乐; ; ;M< ; ; ;任何库B></亚科学记数法。


从http:/ / / / / D输入blogs.oracle.com subnormal _号码:

There are potentially multiple ways of representing the same number,
using decimal as an example, the number 0.1 could be represented as
1*10-1 or 0.1*100 or even 0.01 * 10. The standard dictates that the
numbers are always stored with the first bit as a one. In decimal that
corresponds to the 1*10-1 example.

Now suppose that the lowest exponent that can be represented is -100.
So the smallest number that can be represented in normal form is
1*10-100. However, if we relax the constraint that the leading bit be
a one, then we can actually represent smaller numbers in the same
space. Taking a decimal example we could represent 0.1*10-100. This
is called a subnormal number. The purpose of having subnormal numbers
is to smooth the gap between the smallest normal number and zero.

It is very important to realise that subnormal numbers are represented
with less precision than normal numbers. In fact, they are trading
reduced precision for their smaller size. Hence calculations that use
subnormal numbers are not going to have the same precision as
calculations on normal numbers. So an application which does
significant computation on subnormal numbers is probably worth
investigating to see if rescaling (i.e. multiplying the numbers by
some scaling factor) would yield fewer subnormals, and more accurate
results.


IEEE 754基础好的。

首先,让我们回顾一下IEEE754数字的基本结构。好的。

我们将重点放在单精度(32位),但所有内容都可以立即推广到其他精度。好的。

格式为:好的。

  • 1位:符号
  • 8位:指数
  • 23位:分数

或者如果你喜欢图片:好的。

enter image description here。好的。

来源。好的。

符号很简单:0为正,1为负,故事结束。好的。

指数是8位长,所以它的范围是0到255。好的。

指数称为有偏指数,因为它的偏移量为-127,例如:好的。

1
2
3
4
5
6
7
8
9
10
11
  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

前导位约定好的。

在设计IEEE754时,工程师们注意到,除了0.0以外,所有的数字都有一个二进制的1作为第一个数字。好的。

例如。:好的。

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

两者都是从恼人的1.部分开始的。好的。

因此,几乎每个数字都占用一个精度位是浪费的。好的。

为此,他们创建了"前导位约定":好的。

always assume that the number starts with one

Ok.

但是如何处理0.0呢?嗯,他们决定创建一个例外:好的。

  • 如果指数为0
  • 分数是0
  • 那么这个数字表示正负两个数:0.0

所以字节00 00 00 00也代表0.0,看起来不错。好的。

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

  • 指数:0
  • 分数:1

由于前导位的约定,它在十六进制小数中看起来像这样:好的。

1
1.000002 * 2 ^ (-127)

其中,.000002是22个零,末尾是1。好的。

我们不能取fraction = 0,否则这个数字就是0.0。好的。

但是工程师们也有敏锐的艺术头脑,他们想:那不是很难看吗?我们从直线的0.0跳到一个甚至不是2的适当幂的东西?我们不能代表更小的数字吗?好的。

低能数好的。

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

If the exponent is 0, then:

Ok.

  • the leading bit becomes 0
  • the exponent is fixed to -126 (not -127 as if we didn't have this exception)

Such numbers are called subnormal numbers (or denormal numbers which is synonym).

Ok.

此规则立即暗示该数字如下:好的。

  • 指数:0
  • 分数:0

0.0,它有点优雅,因为它意味着少了一个规则来跟踪。好的。

所以根据我们的定义,0.0实际上是一个次正规数!好的。

根据这个新规则,最小的非次正规数是:好的。

  • 指数:1(0将是次正规)
  • 分数:0

代表:好的。

1
1.0 * 2 ^ (-126)

那么,最大的次正规数是:好的。

  • 指数:0
  • 分数:0x7fffff(23位1)

等于:好的。

1
0.FFFFFE * 2 ^ (-126)

其中,.FFFFFE再次是点右侧的23位1。好的。

这非常接近最小的非次正规数,听起来很正常。好的。

最小的非零次正规数是:好的。

  • 指数:0
  • 分数:1

等于:好的。

1
0.000002 * 2 ^ (-126)

它看起来也非常接近于0.0!好的。

工程师们找不到任何合理的方法来表示比这小的数字,他们很高兴,回到网上看猫的图片,或者70年代他们做的任何事情。好的。

如您所见,次正规数在精度和表示长度之间进行权衡。好的。

作为最极端的例子,最小的非零次正规:好的。

1
0.000002 * 2 ^ (-126)

基本上只有一个位而不是32位的精度。例如,如果我们将其除以2:好的。

1
0.000002 * 2 ^ (-126) / 2

我们真的到了江户十一〔一〕号!好的。

可视化好的。

对于我们所学的东西,有一个几何直觉总是一个好主意,下面是。好的。

如果我们为每个给定的指数在一条线上绘制IEEE754浮点数,它看起来是这样的:好的。

1
2
3
4
5
6
7
8
9
10
11
          +---+-------+---------------+-------------------------------+
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。好的。

如果没有次法线,假设它看起来像:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
          +---+---+-------+---------------+-------------------------------+
exponent  | ? | 0 |   1   |       2       |               3               |
          +---+---+-------+---------------+-------------------------------+
          |   |   |       |               |                               |
          v   v   v       v               v                               v
          -----------------------------------------------------------------
floats    *   ***** * * * *   *   *   *   *       *       *       *       *
          -----------------------------------------------------------------
          ^   ^   ^       ^               ^                               ^
          |   |   |       |               |                               |
          0   |   2^-126  2^-125          2^-124                          2^-123
              |
              2^-127

对于次法线,如下所示:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
          +-------+-------+---------------+-------------------------------+
exponent  |   0   |   1   |       2       |               3               |
          +-------+-------+---------------+-------------------------------+
          |       |       |               |                               |
          v       v       v               v                               v
          -----------------------------------------------------------------
floats    * * * * * * * * *   *   *   *   *       *       *       *       *
          -----------------------------------------------------------------
          ^   ^   ^       ^               ^                               ^
          |   |   |       |               |                               |
          0   |   2^-126  2^-125          2^-124                          2^-123
              |
              2^-127

通过比较这两个图,我们发现:好的。

  • 次正规线是指数0范围的两倍,从[2^-127, 2^-126)[0, 2^-126)。好的。

    次正常范围内浮动之间的间距与[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表示单精度IEEE754浮点数。好的。

尤其是我的Ubuntu 18.04 AMD64联想P51笔记本电脑。好的。

基于这个假设,所有断言都传递给以下程序:好的。

低于正常值C好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#if __STDC_VERSION__ < 201112L
#error C11 required
#endif

#ifndef __STDC_IEC_559__
#error IEEE 754 not implemented
#endif

#include
#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"
"
,
        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上游。好的。

编译和运行时使用:好的。

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

C++好的。

除了暴露所有C的API之外,C++还公开了一些额外的低于正常的相关功能,这些功能在EDCOX1(0)中不容易在C中获得,例如:好的。

  • denorm_min:返回t类型的最小正次正态值

在C++中,为每个浮点类型绘制空穴API,并且更好。好的。

启动位置好的。

x86 U64和ARMV8直接在硬件上实现了IEEE754,C代码将其转换为。好的。

在某些实现中,子规范似乎不如规范快:为什么将0.1f更改为0会将性能降低10倍?这在ARM手册中提到,请参阅此答案的"ARMV8详细信息"部分。好的。

ARMV8细节好的。

ARM体系结构参考手册ARMV8 DDI 0487C。手册A1.5.4"齐平到零"描述了一种可配置模式,其中子法线四舍五入为零以提高性能:好的。

The performance of floating-point processing can be reduced when doing calculations involving denormalized numbers and Underflow exceptions. In many algorithms, this performance can be recovered, without significantly affecting the accuracy of the final result, by replacing the denormalized operands and intermediate results with zeros. To permit this optimization, ARM floating-point implementations allow a Flush-to-zero mode to be used for different floating-point formats as follows:

Ok.

  • For AArch64:

    Ok.

    • If FPCR.FZ==1, then Flush-to-Zero mode is used for all Single-Precision and Double-Precision inputs and outputs of all instructions.

      Ok.

    • If FPCR.FZ16==1, then Flush-to-Zero mode is used for all Half-Precision inputs and outputs of floating-point instructions, other than:—Conversions between Half-Precision and Single-Precision numbers.—Conversions between Half-Precision and Double-Precision numbers.

      Ok.

A1.5.2"浮点标准和术语"表A1-3"浮点术语"确认了次正规和非正规是同义词:好的。

1
2
3
4
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 eu el1,aarch32 media and vfp feature register 1"表明非规范支持实际上是完全可选的,并提供了一些检测是否支持:好的。

FPFtZ, bits [3:0]

Ok.

Flush to Zero mode. Indicates whether the floating-point implementation provides support only for the Flush-to-Zero mode of operation. Defined values are:

Ok.

  • 0b0000 Not implemented, or hardware supports only the Flush-to-Zero mode of operation.

    Ok.

  • 0b0001 Hardware supports full denormalized number arithmetic.

    Ok.

All other values are reserved.

Ok.

In ARMv8-A, the permitted values are 0b0000 and 0b0001.

Ok.

这表明,当子规范没有实现时,实现将恢复为零。好的。

无穷大和NaN好的。

好奇吗?我写了一些东西在:好的。

  • 无穷大:C语言中浮点数据类型的范围?
  • 南:安静的南和发出信号的南有什么区别?

好啊。