C++ Std::stof 不适用于小于 FLT_MIN 的浮点数?
C++ Std::stof doesn't work for float less than FLT_MIN?
我遇到以下代码行问题:
float temp = std::stof("-8.34416e-46");
我的程序一到达这一行就中止了。明明这个浮点数小于FLT_MIN,但是这样的浮点数是允许存在的。 (例如,float temp = -8.34416e-46;
工作正常。stof
方法应该只适用于 FLT_MIN 和 FLT_MAX 之间的值吗?
如果是这样,将字符串 ("-8.34416e-46") 转换为浮点数的替代方法是什么?
另一种选择
用std::stod
转换为double
,然后分配给float
。如果需要,添加边界检查。
转换“-8.34416e-46”
C++ 标准允许将字符串转换为 float
以在结果处于次正常范围内时报告下溢,即使它是可表示的。
当使用舍入到最接近的可表示值时,−8.34416•10−46 在 float
的范围内(在使用 IEEE-754 的 C++ 实现中float
的 binary32,这很常见),但它处于次正常范围内。 C++ 标准说 stof
调用 strtof
然后遵从 C 标准来定义 strtof
。 C 标准表明 strtof
可能会下溢,关于它说“如果数学结果的量级太小以至于无法在指定的对象中表示数学结果,而没有异常舍入误差,则结果下溢类型。”这是一个笨拙的措辞,但它指的是遇到次正规值时发生的舍入误差。 (低于正常值的相对误差比正常值大,因此它们的舍入误差可以说是非常大的。)
因此,C++ 标准允许 C++ 实现对次正规值进行下溢,即使它们是可表示的。
binary32格式中最小正数为2−149,约1.4•10−45。 8.34416•10−46比这个小,但大于2−149的一半。这意味着,在 0 和 2−149 之间,它更接近后者,因此四舍五入到最接近的可表示值的转换将产生 2−149 而不是零。不幸的是,您的 strtof
实施选择报告下溢,而不是完成到最接近的可表示值的转换。
正常值和低于正常值
对于IEEE-754 32位二进制浮点数,正常范围是2-126到2128-2 104。在这个范围内,每个数字都用一个符号(浮点表示的小数部分)表示,它有一个前导 1 位,后面跟着 23 个附加位,所以当这个范围内的任何实数四舍五入到最近的可表示值最多是前导位位置值的 2-24 倍。
除了这个正常范围之外,还有一个从 2−149 到 2−126−2[=43 的次正常范围=]−149。在这个区间内,浮点格式的指数部分已经达到最小值,不能再减少了。为了表示此区间中越来越小的数字,有效数减少到正常最小值 1 以下。它以 0 开头,后面跟着 23 个附加位。在此区间内,将实数四舍五入到最接近的可表示值时发生的误差可能大于前导位位置值的2-24倍。由于指数不能进一步减少,因此该区间中的数字随着变得越来越小而增加前导 0 位的数量。因此,使用这些数字所涉及的相对错误会增加。
无论出于何种原因,C++ 都表示实现可能会在此时间间隔内报告下溢。 (IEEE-754 标准以复杂的方式定义了下溢,也允许实现一些选择。)
我遇到以下代码行问题:
float temp = std::stof("-8.34416e-46");
我的程序一到达这一行就中止了。明明这个浮点数小于FLT_MIN,但是这样的浮点数是允许存在的。 (例如,float temp = -8.34416e-46;
工作正常。stof
方法应该只适用于 FLT_MIN 和 FLT_MAX 之间的值吗?
如果是这样,将字符串 ("-8.34416e-46") 转换为浮点数的替代方法是什么?
另一种选择
用std::stod
转换为double
,然后分配给float
。如果需要,添加边界检查。
转换“-8.34416e-46”
C++ 标准允许将字符串转换为 float
以在结果处于次正常范围内时报告下溢,即使它是可表示的。
当使用舍入到最接近的可表示值时,−8.34416•10−46 在 float
的范围内(在使用 IEEE-754 的 C++ 实现中float
的 binary32,这很常见),但它处于次正常范围内。 C++ 标准说 stof
调用 strtof
然后遵从 C 标准来定义 strtof
。 C 标准表明 strtof
可能会下溢,关于它说“如果数学结果的量级太小以至于无法在指定的对象中表示数学结果,而没有异常舍入误差,则结果下溢类型。”这是一个笨拙的措辞,但它指的是遇到次正规值时发生的舍入误差。 (低于正常值的相对误差比正常值大,因此它们的舍入误差可以说是非常大的。)
因此,C++ 标准允许 C++ 实现对次正规值进行下溢,即使它们是可表示的。
binary32格式中最小正数为2−149,约1.4•10−45。 8.34416•10−46比这个小,但大于2−149的一半。这意味着,在 0 和 2−149 之间,它更接近后者,因此四舍五入到最接近的可表示值的转换将产生 2−149 而不是零。不幸的是,您的 strtof
实施选择报告下溢,而不是完成到最接近的可表示值的转换。
正常值和低于正常值
对于IEEE-754 32位二进制浮点数,正常范围是2-126到2128-2 104。在这个范围内,每个数字都用一个符号(浮点表示的小数部分)表示,它有一个前导 1 位,后面跟着 23 个附加位,所以当这个范围内的任何实数四舍五入到最近的可表示值最多是前导位位置值的 2-24 倍。
除了这个正常范围之外,还有一个从 2−149 到 2−126−2[=43 的次正常范围=]−149。在这个区间内,浮点格式的指数部分已经达到最小值,不能再减少了。为了表示此区间中越来越小的数字,有效数减少到正常最小值 1 以下。它以 0 开头,后面跟着 23 个附加位。在此区间内,将实数四舍五入到最接近的可表示值时发生的误差可能大于前导位位置值的2-24倍。由于指数不能进一步减少,因此该区间中的数字随着变得越来越小而增加前导 0 位的数量。因此,使用这些数字所涉及的相对错误会增加。
无论出于何种原因,C++ 都表示实现可能会在此时间间隔内报告下溢。 (IEEE-754 标准以复杂的方式定义了下溢,也允许实现一些选择。)