C++ 什么是次正常浮点数?
说明: 确定给定的浮点数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。这 被称为次正常数。具有次正常数的目的 是平滑最小法向数和零之间的间隙 认识到代表次正常数字是非常重要的 精度低于正常数字。事实上,他们正在交易 由于尺寸较小,精度降低。因此,使用 低于正常值的数字将不会有与之相同的精度 正常数的计算。所以一个应用程序 对次正常数的重要计算可能是值得的 调查是否重新缩放(即将数字乘以 一些比例因子)将产生更少的次正常值,并且更精确 结果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. 但是,如果我们放松前导位的约束 一
在IEEE754标准中,浮点数表示为二进制科学符号,x=M×2e。这里M是尾数,e是指数。从数学上讲,您可以选择指数,使1≤MIEEE 754基础 首先,让我们回顾一下IEEE 754的基础知识,这些数字是有组织的 我们将重点讨论单精度(32位),但任何东西都可以立即推广到其他精度 格式为:
- 1位:符号
- 8位:指数
- 23位:分数
-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