以 32 个十进制数字分析浮点数/双精度
Analysis of float/double precision in 32 decimal digits
从另一个人的.c文件中,我看到了这个:
const float c = 0.70710678118654752440084436210485f;
他想避免计算sqrt(1/2)
.
这真的可以用普通C/C++
以某种方式存储吗?我的意思是不要失去精度。这对我来说似乎是不可能的。
我正在使用C++,但我不认为这两种语言之间的精度差异太大(如果有的话(,这就是我没有测试它的原因。
所以,我写了这几行,看看代码的行为:
std::cout << "Number: 0.70710678118654752440084436210485n";
const float f = 0.70710678118654752440084436210485f;
std::cout << "float: " << std::setprecision(32) << f << std::endl;
const double d = 0.70710678118654752440084436210485; // no f extension
std::cout << "double: " << std::setprecision(32) << d << std::endl;
const double df = 0.70710678118654752440084436210485f;
std::cout << "doublef: " << std::setprecision(32) << df << std::endl;
const long double ld = 0.70710678118654752440084436210485;
std::cout << "l double: " << std::setprecision(32) << ld << std::endl;
const long double ldl = 0.70710678118654752440084436210485l; // l suffix!
std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl;
输出是这样的:
* ** ***
v v v
Number: 0.70710678118654752440084436210485 // 32 decimal digits
float: 0.707106769084930419921875 // 24 >> >>
double: 0.70710678118654757273731092936941
doublef: 0.707106769084930419921875 // same as float
l double: 0.70710678118654757273731092936941 // same as double
l doublel: 0.70710678118654752438189403651592 // suffix l
其中*
是float
的最后一个精确数字,**
double
的最后一个准确数字,***
long double
的最后一个准确数字。
double
的输出有 32 个十进制数字,因为我已将std::cout
的精度设置为该值。
正如预期的那样,float
输出有 24,如下所示:
float has 24 binary bits of precision, and double has 53.
我希望最后一个输出与最后一个输出相同,即f
后缀不会阻止数字成为double
。我认为当我写这篇文章时:
const double df = 0.70710678118654752440084436210485f;
发生的情况是,首先数字变成float
数字,然后存储为double
,因此在第 24 位十进制数字之后,它有零,这就是为什么double
精度停在那里的原因。
我说的对吗?
从这个答案中,我找到了一些相关信息:
float x = 0 has an implicit typecast from int to float.
float x = 0.0f does not have such a typecast.
float x = 0.0 has an implicit typecast from double to float.
[编辑]
关于__float128
,它不是标准的,因此它不在竞争中。在此处查看更多内容。
从标准:
有三种浮点类型:浮点型、双精度型和长型双精度型。 双精度类型至少提供与浮点数一样多的精度,并且 长双精度型提供至少与双精度一样多的精度。这 浮点类型的值集是 双倍类型;双精度类型的值集是 长双精度类型的值集。的值表示形式 浮点类型是实现定义的。
所以你可以看到你对这个问题的问题:标准实际上并没有说浮点数有多精确。
就标准实现而言,您需要查看IEEE754,这意味着Irineau和Davidmh的另外两个答案是解决问题的完全有效的方法。
至于后缀字母表示类型,再看标准:
浮动文本的类型为双精度,除非由 后缀。后缀 f 和 F 指定浮点数,后缀 l 和 L 指定 长双。
因此,除非您使用L
后缀,否则您尝试创建long double
的精度将与您分配给它的double
文本具有相同的精度。
我知道其中一些答案可能看起来并不令人满意,但在您可以驳回答案之前,需要对相关标准进行大量背景阅读。这个答案已经比预期的要长了,所以我不会在这里尝试解释所有内容。
最后要注意的是:既然精度没有明确定义,为什么不有一个比需要的更长的常数呢?始终定义一个足够精确的常量以始终可表示,无论类型如何,似乎都是有意义的。
Python 的数字库 numpy 有一个非常方便的浮点信息函数。所有类型都等效于 C:
对于 C 的浮点数:
print numpy.finfo(numpy.float32)
Machine parameters for float32
---------------------------------------------------------------------
precision= 6 resolution= 1.0000000e-06
machep= -23 eps= 1.1920929e-07
negep = -24 epsneg= 5.9604645e-08
minexp= -126 tiny= 1.1754944e-38
maxexp= 128 max= 3.4028235e+38
nexp = 8 min= -max
---------------------------------------------------------------------
对于 C 的双精度:
print numpy.finfo(numpy.float64)
Machine parameters for float64
---------------------------------------------------------------------
precision= 15 resolution= 1.0000000000000001e-15
machep= -52 eps= 2.2204460492503131e-16
negep = -53 epsneg= 1.1102230246251565e-16
minexp= -1022 tiny= 2.2250738585072014e-308
maxexp= 1024 max= 1.7976931348623157e+308
nexp = 11 min= -max
---------------------------------------------------------------------
对于 C 的长浮点数:
print numpy.finfo(numpy.float128)
Machine parameters for float128
---------------------------------------------------------------------
precision= 18 resolution= 1e-18
machep= -63 eps= 1.08420217249e-19
negep = -64 epsneg= 5.42101086243e-20
minexp=-16382 tiny= 3.36210314311e-4932
maxexp= 16384 max= 1.18973149536e+4932
nexp = 15 min= -max
---------------------------------------------------------------------
因此,即使是长浮点数(128 位(也不会为您提供所需的 32 位数字。但是,你真的需要它们吗?
一些编译器具有binary128
浮点格式的实现,由IEEE 754-2008规范化。例如,使用 gcc,类型为 __float128
。该浮点格式具有大约 34 个十进制精度 (log(2^113)/log(10)
(。
您可以使用 Boost 多精度库来使用其包装器 float128。该实现将使用本机类型(如果可用(,或使用插入式替换。
让我们用最新的 g++ (4.8( 扩展您的实验,使用新的非标准类型 __float128
:
// Compiled with g++ -Wall -lquadmath essai.cpp
#include <iostream>
#include <iomanip>
#include <quadmath.h>
#include <sstream>
std::ostream& operator<<(std::ostream& out, __float128 f) {
char buf[200];
std::ostringstream format;
format << "%." << (std::min)(190L, out.precision()) << "Qf";
quadmath_snprintf(buf, 200, format.str().c_str(), f);
out << buf;
return out;
}
int main() {
std::cout.precision(32);
std::cout << "Number: 0.70710678118654752440084436210485n";
const float f = 0.70710678118654752440084436210485f;
std::cout << "float: " << std::setprecision(32) << f << std::endl;
const double d = 0.70710678118654752440084436210485; // no f extension
std::cout << "double: " << std::setprecision(32) << d << std::endl;
const double df = 0.70710678118654752440084436210485f;
std::cout << "doublef: " << std::setprecision(32) << df << std::endl;
const long double ld = 0.70710678118654752440084436210485;
std::cout << "l double: " << std::setprecision(32) << ld << std::endl;
const long double ldl = 0.70710678118654752440084436210485l; // l suffix!
std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl;
const __float128 f128 = 0.70710678118654752440084436210485;
const __float128 f128f = 0.70710678118654752440084436210485f; // f suffix
const __float128 f128l = 0.70710678118654752440084436210485l; // l suffix
const __float128 f128q = 0.70710678118654752440084436210485q; // q suffix
std::cout << "f128: " << f128 << std::endl;
std::cout << "f f128: " << f128f << std::endl;
std::cout << "l f128: " << f128l << std::endl;
std::cout << "q f128: " << f128q << std::endl;
}
输出为:
* ** *** ****
v v v v
Number: 0.70710678118654752440084436210485
float: 0.707106769084930419921875
double: 0.70710678118654757273731092936941
doublef: 0.707106769084930419921875
l double: 0.70710678118654757273731092936941
l doublel: 0.70710678118654752438189403651592
f128: 0.70710678118654757273731092936941
f f128: 0.70710676908493041992187500000000
l f128: 0.70710678118654752438189403651592
q f128: 0.70710678118654752440084436210485
其中*
是float
的最后一个精确数字,**
最后一个精确数字 double
,***
long double
的最后一个精确数字,****
是__float128
的最后一个准确数字。
正如另一个答案所说,C++标准没有说明各种浮点类型的精度是多少(就像它没有说明整数类型的大小是多少(。它仅指定这些类型的最小精度/大小。但规范IEEE754确实指定了所有这些!所有许多架构的 FPU 确实实现了该规范IEEE745,最新版本的 gcc 通过扩展__float128
实现了规范的类型binary128
。
至于你或我的代码的解释,像0.70710678118654752440084436210485f
这样的表达式是浮点文字。它有一个类型,由其后缀定义,这里f
float
.因此,文字的值对应于给定数字中给定类型的最接近值。这就解释了为什么,例如,代码中"doublef"的精度与"float"的精度相同。在最近的 gcc 版本中,有一个扩展,它允许定义类型 __float128
的浮点文字 ,带有Q
后缀(四精度(。
- 在C++中序列化浮点数/双精度,编译为 WebAssembly
- 如何提高计算浮点数的精度?
- 我们如何清楚地知道 C/C++ 中的双精度或浮点数的精度?
- 这是为浮点数/双精度函数泛型的正确方法吗?
- 关于在C/C++中比较整数和浮点/双精度
- 如何在没有浮点数/双精度数的情况下生成均匀和高斯分布的伪随机数
- 具有 IEEE 754 浮点双精度数据类型的安全往返整数值
- 将浮点浮点/双精度的其余部分转换为整数
- 为什么在C和C++中没有浮点/双精度的除法余数运算
- 使用浮点/双精度作为循环变量
- 字符数据浮点数/双精度数
- 宏或函数,用于根据给定的符号、尾数和指数构造浮点(双精度)
- C++将SATOSHI(uint64_t)转换为不带浮点/双精度的BTC(string)
- C++将字符串转换为浮点/双精度,而不会丢失数据
- 将输入字符串转换为浮点/双精度C++
- 在运行时定义双精度或浮点数的精度/大小
- 整数和分数为浮点数/双精度
- 在c++中控制浮点数的精度
- c++中浮点数/双精度数的比较
- 以 32 个十进制数字分析浮点数/双精度