考虑以下代码
#include <iostream>
int main()
{
const std::string s("5.87747175e-39");
float f = std::stof(s);
std::cout << s << " - " << f << std::endl;
return 0;
}
使用 g++ 编译并执行时,会抛出。但是,正如您在此处std::out_of_range
看到的,5.87747175e-39 是一个有效的浮点数。这是怎么回事?
以防万一,我使用的是 amd64 Debian 12,它使用 g++ 版本 12.2
不管怎样,Python 认为这是一个有效的浮点数
python3 -c 'print(float("5.87747175e-39"))'
此外,这个问题似乎扩展到所有非规范化浮点数。例如,“1.1754942e-38”是最大的有效IEEE-754 非规范化浮点数,但 stof() 认为它是一个错误。最小的规范化浮点数是“1.1754944e-38”,也就是报告为 FLT_MIN
查看GCC 的实现,
std::stof
如果浮点数小于,则抛出此错误FLT_MIN
,否则FLT_TRUE_MIN
。造成这种情况的原因可能是因为是在C++17
FLT_TRUE_MIN
中引入的,而在C++11中。std::stof
float
对于低于正常范围的结果,可能会报告下溢,因为此类结果可能具有比正常更大的错误。如果结果处于低于正常范围,C++ 标准允许将字符串转换为报告下溢。5.87747175•10 −39在 的范围内
float
,但它在次正常范围内(如下所述)。C++ 标准规定调用stof
(strtof
在 C++ 2020 草案 N4849 21.3.4 [string.conversions] 3 中),然后遵从 C 标准来定义strtof
。C 标准指出strtof
可能会下溢。C 2018 7.12.1 6 规定“如果数学结果的幅度太小,以至于无法在指定类型的对象中表示数学结果,而不会产生异常舍入误差,则结果会发生下溢。” “异常舍入误差”是指遇到次正常值时发生的舍入误差。(次正常值的有效有效数字位较少,因此比正常值更容易出现相对误差,所以它们的舍入误差可以说是异常的。)因此,C++ 标准允许 C++ 实现对次正规值进行下溢,即使这些值是可表示的。
这个问题本质上是这个问题的重复,只是一个问题是针对的
double
,而这个问题是针对的float
。正常值和低于正常值
对于 IEEE-754 32 位二进制浮点数,正常范围是从 2 −126到 2 128 −2 104。在此范围内,每个数字都用一个有效数字(浮点表示的小数部分)表示,其前导位为 1,后跟 23 个附加位,因此将此范围内的任何实数四舍五入为最接近的可表示值时发生的误差最多为前导位位置值的2 −24倍。
除了这个正常范围之外,还有一个从 2 −149到 2 −126 −2 −149的亚正常范围。在这个区间内,浮点格式的指数部分已经达到了最小值,并且无法再减少。为了在这个区间内表示越来越小的数字,有效数字要降低到正常最小值 1 以下。它以 0 开头,后面跟着 23 个附加位。在这个区间内,将实数四舍五入到最接近的可表示值时出现的误差可能大于前导位位置值的 2 −24倍。由于指数无法再减少,这个区间内的数字随着它们越来越小,前导 0 位数也会增加。因此,使用这些数字所涉及的相对误差就会增加。
根据std::numeric_limits::min
的输出为。
float
这与 的记录值1.17549e-38
一致。FLT_MIN