将 C 字符串转换为双精度数或从双精度数转换时出现奇怪的行为
我无法理解 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
我的问题是:
- 为什么 x 打印为非零数字?我知道编译器有时出于计算目的将双精度类型提升为更高精度的类型,但是 printf 不应该将 x 视为 64 位双精度类型吗?
- 如果 C 库秘密使用扩展精度浮点数,为什么 strtod 在尝试转换这些小数字时设置 errno?为什么它会产生正确的结果呢?
- 这种行为只是一个错误,是我的特定硬件和开发环境的结果吗? (不幸的是,我目前无法在其他平台上进行测试。)
感谢您提供的任何帮助。当我收到反馈时,我会尽力澄清这个问题。
I'm having trouble understanding C's rules for what precision to assume when printing doubles, or when converting strings to doubles. The following program should illustrate my point:
#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;
}
The output I get when I compile and run this program (on a Core 2 Duo with gcc 4.5.2) is:
DBL_MIN = 2.225074e-308, x = 2.225074e-310
Error converting '1e-310': Numerical result out of range
y = 1.000000e-310
My questions are:
- Why is x printed as a nonzero number? I know compilers sometimes promote doubles to higher precision types for the purposes of computation, but shouldn't printf treat x as a 64-bit double?
- If the C library is secretly using extended precision floating point numbers, why does strtod set errno when trying to convert these small numbers? And why does it produce the correct result anyway?
- Is this behavior just a bug, a result of my particular hardware and development environment? (Unfortunately I'm not able to test on other platforms at the moment.)
Thanks for any help you can give. I will try to clarify the issue as I get feedback.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
由于 IEEE 中存在非正规数 754标准。
DBL_MIN
是最小的标准化值。因为标准是这么说的(C99 7.20.1.3):
<块引用>
如果
结果下溢(7.12.1),函数返回一个大小不大于的值
比返回类型中最小的标准化正数; errno 是否获取
值 ERANGE 是实现定义的。
返回“正确”值(即 1e-310)遵循上述约束。
所以不是一个错误。这在技术上是依赖于平台的,因为 C 标准对非正规数的存在或行为没有提出要求 (AFAIK)。
Because of the existence of denormal numbers in the IEEE-754 standard.
DBL_MIN
is the smallest normalised value.Because the standard says so (C99 7.20.1.3):
Returning the "correct" value (i.e. 1e-310) obeys the above constraint.
So not a bug. This is technically platform-dependent, because the C standard(s) place no requirements on the existence or behaviour of denormal numbers (AFAIK).
以下是标准对于
strtod
下溢的规定(C99,7.20.1.3p10)“如果结果下溢(7.12.1),则函数返回一个值,其大小不大于最小归一化正值返回类型中的数字;errno 是否获取值 ERANGE 是实现定义的。”
关于
strtod
下溢上的ERANGE
,这是 glibc 所说的“当发生下溢时,引发下溢异常,并返回零(适当签名)。errno 可以设置为错误,但这并不能保证。”
http://www.gnu.org/savannah-checkouts/ gnu/libc/manual/html_node/Math-Error-Reporting.html
(请注意,此页面明确链接到 glibc
strtod
页面“Parsing of漂浮”:http://www.gnu.org/savannah-checkouts/ gnu/libc/manual/html_node/Parsing-of-Floats.html
Here is what the standard says for
strtod
underflow (C99, 7.20.1.3p10)"If the result underflows (7.12.1), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined."
Regarding
ERANGE
onstrtod
underflow, here is what glibc says"When underflow occurs, the underflow exception is raised, and zero (appropriately signed) is returned. errno may be set to ERANGE, but this is not guaranteed."
http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Math-Error-Reporting.html
(Note that this page is explicitly linked on glibc
strtod
page "Parsing of Floats":http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Parsing-of-Floats.html