将C字符串转换为双精度浮点数或将双精度浮点数转换为C字符串时出现异常行为

7

我在理解C语言中关于打印双精度浮点数时应该采用什么精度,或者将字符串转换为双精度浮点数时遇到了困难。下面的程序可以说明我的问题:

#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    double x, y;
    const char *s = "1e-310";

    /* Should print zero */
    x = DBL_MIN/100.;
    printf("DBL_MIN = %e, x = %e\n", DBL_MIN, x);

    /* Trying to read in floating point number smaller than DBL_MIN gives an error */
    y = strtod(s, NULL);
    if(errno != 0)
        printf("  Error converting '%s': %s\n", s, strerror(errno));
    printf("y = %e\n", y);

    return 0;
}

我在Core 2 Duo上使用gcc 4.5.2编译并运行此程序时得到的输出结果如下:

DBL_MIN = 2.225074e-308, x = 2.225074e-310
  Error converting '1e-310': Numerical result out of range
y = 1.000000e-310

我的问题是:

  1. 为什么x被打印成非零数?我知道编译器有时会将double类型提升到更高精度的类型进行计算,但printf不应该将x视为64位double吗?
  2. 如果C库秘密使用扩展精度浮点数,为什么在尝试转换这些小数时strtod会设置errno?为什么它还会产生正确的结果?
  3. 这种行为只是一个错误,是由于我的特定硬件和开发环境造成的吗?(不幸的是,我目前无法在其他平台上进行测试。)

感谢您能给予的任何帮助。我会在得到反馈后尽力澄清问题。


首先,DBL_MIN = 2.225074e-308 并没有太多意义,因为 IEEE DP 的最小值是 4.94066e-324。这就解释了为什么除以 100 仍然可以正确运行。但问题是为什么 DBL_MIN 不是 4.94066e-324 - Mysticial
澄清如下:DBL_MIN是最小的_规范化_值。 - Jordan Carlson
2个回答

8
  1. 由于IEEE-754标准中存在非规范化数,所以DBL_MIN是最小的规范化值。

  2. 因为标准这样规定(C99 7.20.1.3):

    如果结果下溢(7.12.1),函数返回一个其绝对值不大于返回类型中最小规范化正数的值;errno是否获得ERANGE的值是实现定义的。

    返回“正确”的值(即1e-310)遵从上述约束。

  3. 所以这不是bug。这在技术上取决于平台,因为C标准对非规范化数的存在或行为没有任何要求(据我所知)。


1
谢谢,这是非常有帮助的信息。我有点希望strtod在这种情况下不会标记错误,但我能理解其原因。我会找到解决方法。 - Jordan Carlson

7
这是标准中关于strtod下溢(C99, 7.20.1.3p10)的说明:
“如果结果下溢(7.12.1),函数返回的值的大小不超过返回类型中最小的规格化正数;errno是否获取值ERANGE是实现定义的。”
关于strtod下溢的ERANGE,这是glibc的说法:
“当发生下溢时,会引发下溢异常,并返回零(适当的符号)。errno可能设置为ERANGE,但这不是保证的。”

http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Math-Error-Reporting.html

请注意,此页面明确链接到 glibc 的 strtod 页面“解析浮点数”: http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Parsing-of-Floats.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接