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;
}

当我编译和运行这个程序(在带有 gcc 4.5.2 的 Core 2 Duo 上)时得到的输出是:

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

我的问题是:

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

谢谢你提供的所有帮助。当我得到反馈时,我会尝试澄清这个问题。

4

2 回答 2

8
  1. 因为 IEEE-754 标准中存在非正规数DBL_MIN是最小的归一化值。

  2. 因为标准是这样说的(C99 7.20.1.3):

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

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

  3. 所以不是错误。这在技术上取决于平台,因为 C 标准对非正规数 (AFAIK) 的存在或行为没有任何要求。

于 2012-01-09T20:23:46.693 回答
7

这是标准对strtod下溢的规定(C99,7.20.1.3p10)

“如果结果下溢(7.12.1),函数返回一个值,其大小不大于返回类型中最小的标准化正数;errno 是否获取值 ERANGE 是实现定义的。”

关于ERANGEstrtod溢,这是 glibc 所说的

“发生下溢时,引发下溢异常,并返回零(适当签名)。errno 可以设置为 ERANGE,但这不能保证。”

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

(请注意,此页面在 glibcstrtod页面“Parsing of Floats”上明确链接: http ://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Parsing-of-Floats.html

于 2012-01-09T20:24:49.413 回答