贡献者: addis
另见 “双精度和变精度浮点数测试(Matlab)”。
0.
(普通的 0.
就是 +0.
)和 -0.
,注意一定要有 “.
”,整数零不区分正负),例如 1/0., 1/(-0.)
分别得到 inf, -inf
,反之亦然。另外一些函数也对 -0.
敏感,例如 atan2
。
0. == -0.
,-2*0.
得 -0.
,inf == inf
,-inf == -inf
,inf * 2 == inf
,inf * 0 == nan
。
nan
和自身都不相等,可以用这种方法来判断一个浮点数是否是 nan
。也可以用 isnan()
。
C++ 浮点相关函数:
bool signbit(x)
获取符号位(对于 -0
来说,这不同于 (x < 0)
)
ldexp(x, n)
计算 double man = frexp(x, &exp2)
可以拆分 mantissa 和 exp。其中 0.5 <= man < 1
;
int fpclassify(x)
可以用来给浮点数归类(返回 FP_NORMAL, FP_SUBNORMAL, FP_ZERO, FP_INFINITE, FP_NAN
之一)。
bool isfinite(x)
检查是否是 FP_NORMAL, FP_SUBNORMAL, FP_ZERO
之一。
bool isnormal(x)
检查 fpclassify
是否返回 FP_NORMAL
。
unit in the last place (ULP) 就是 mantissa 增加 1 bit 时表示的数增加多少。这等于 eps
。
所以双精度的 mantissa 表示 1 时,相对精度就是 eps
,表示 1-eps
时,相对精度就非常接近 eps/2
(最小相对误差)。所以一般来说相对精度在二者之间。但一个例外就是当绝对值非常小时(小于
需要多少位十进制数可以通过四舍五入精确表示任意的双精度数呢?eps/2
(
那么一个 double 能不能精确地用十进制表示呢?可以的,因为 eps
可以,但可能需要非常多的位数尤其是 2 的指数非常大或者非常小的时候可能会需要几百位,没有太大必要。
在 x86 机器上,long double
一般是 1 bit 符号 + 15 bit 指数 + 64 bit 小数 = 80 bit,十进制大约是 18-19 位有效数字2。虽然 sizeof
是 16 字节,但实际上只使用了 10 字节3。long double
的运算是硬件支持的,所以比较快。
然而 sqrt
函数似乎并不会提高精度(仍然是 16 位):
另外测试一下双精度的二进制表示(图 3 ),例如双精度的 10
表示为
10000000010
表示
理论上指数部分可以表示 0
除了第一 bit 其他都必须是 0)。当指数为 inf
(要求小数部分都是零,正负号仍然有效)或者 nan
(0b1...
表示安静的 qNaN
,0b0..1..
表示会发错误信号的 sNaN
)。
双精度类型可以表示 -2e53
到 2e53
的每一个整数,更小或更大的整数有的也可以表示(事实上此时只可以表示整数),只是不连续。
其中
denorm_min()
比较特殊,当指数 bit 全为 0 的时候,指数为 -1022
而不是 -1023
,且小数部分前面不加 1。这叫做 denominize
。
4使用四精度比使用任意精度类型(如 MPFR 或 Flint 库)要快得多,因为任意精度浮点数是动态分配内存的。
如果想完全利用 16 字节,见 GCC/g++ 编译器的 __float128
类型(文档)。相当于 FORTRAN 的 QUAD real
。有 112 bit 小数,15 bit 指数和 1 bit 正负号(图 1 )。实测的 bit 表示和上面二进制的同理(也存在两个奇怪的地方)。
加上头文件 #include <quadmath.h>
,使用 g++
编译,编译时加上选项 -lquadmath
和 -fext-numeric-literals
即可。
__complex128
是对应的四精度复数类型,相当于两个 __float128
。
给 __complex128
的实部和虚部分别赋值:
std::complex<__real128>
比较好。
另见 “C++ 高精度计算资源列表”。
1. ^ 参考 Wikipedia 相关页面。
2. ^ Visual Studio 中据说 long double 和 double 是一样的。
3. ^ 详见这里 的 x86 extended precision format 一节
4. ^ 参考 Wikipedia 相关页面。
友情链接: 超理论坛 | ©小时科技 保留一切权利