如何在C语言中将double转换成int?

27
double a;
a = 3669.0;
int b;
b = a;

我在b中得到了3668,而不是3669。

我该如何解决这个问题?如果有3559.8之类的数字,我也希望得到3559而不是3560。


11
很有趣。3669.0在浮点数中可以精确表示... - Mysticial
@Mysticial:嗯,是的,那个数字应该以浮点格式存储为3669。可能是编译器的问题。 - Shamim Hafiz - MSFT
1
3668.51应该映射到3669吗?(您是要四舍五入,还是只是剪切非常接近的数字?) - Cascabel
17
代码是错误的。原帖中有一个复杂的计算,作者认为结果应该是“3669.0”,但实际上结果略低于此数值。 - Gabe
我运行了这个程序,在b中得到了3669。这是结果链接:http://www.ideone.com/yWT5G - yo_man
显示剩余3条评论
5个回答

68

我怀疑你其实并没有那个问题 - 我怀疑你真正遇到的是:

double a = callSomeFunction();
// Examine a in the debugger or via logging, and decide it's 3669.0

// Now cast
int b = (int) a;
// Now a is 3668

我这么说的原因是,虽然许多十进制小数不能floatdouble 中精确存储,但对于这种数量级的整数不适用。它们可以非常容易地以二进制浮点形式准确表示。(非常大的整数并不总是能够精确表示,但我们处理的不是非常大的整数。)

我强烈怀疑你的 double 值实际上略小于3669.0,但无论使用什么诊断工具,它都会显示为3669.0。将其转换为整数值只执行截断,而不是四舍五入,因此出现了问题。

假设您的 double 类型是IEEE-754 64位类型,则小于3669.0的最大值恰好是

3668.99999999999954525264911353588104248046875

如果你使用的任何诊断方法中该值显示为3669.0,那么很可能(我会说是有可能)就是这种情况。


1
尝试添加类似 printf("a = %.50f\n", a); 的内容,以查看 a 的实际值。 - Keith Thompson
@KeithThompson:就我个人而言,我使用了一些C#代码来直接处理二进制值 :) - Jon Skeet
6
这是答案。而且是一个很棒的答案!我的问题是:当@Jon Skeet的分数变得太大以至于超过了SO的规模时会发生什么?我们对Jon Skeet的赞赏最终会毁掉SO吗? - Del

7

int 中转换为双精度浮点数? - curiousguy
哦,是的,在int中...抱歉,这是我的错误。 - Jeegar Patel

3

这是臭名昭著的浮点数舍入问题。只需添加一个非常小的数字,即可纠正此问题。

double a;
a=3669.0;
int b;
b=a+ 1e-9;

8
在二进制浮点数中,3669.0可以完全表示,这里不应该存在任何问题。 - Jon Skeet
@JonSkeet: 是的,我同意。对于这个特定的数字,不应该出现问题。我猜可能是编译器有问题,或者 OP 没有提供导致问题的确切情况。 - Shamim Hafiz - MSFT
4
依我之见,后者更有可能发生。 - Jon Skeet

1
如何在 C 中将 double 转换为 int?快速回答。(未满足 OP 的隐含目标需求,即四舍五入。)
// Discard the fractional part of d - truncation.
// Well defined when the truncated d is near the int range.
int i = (int) d;

double转换为int时需要解决一些问题。

  • double值的小数部分接近1.0时,如OP的真实情况下应该怎么办?通常会优先选择一个四舍五入的值。

  • 如何检测/处理超出int范围的double

  • 无穷大和非数字呢?

如果范围检测不重要,则可以使用long lround(double)一步完成四舍五入和转换:“...将其参数四舍五入为最接近的整数值,将中间值向远离零的方向舍入,而不考虑当前的舍入方向”。

#include <math.h>
int = (int) lround(d); // Since C99.

如果范围检测很重要,首先使用双精度数学来检测范围。请注意正确处理边缘情况测试,因为从[INT_MIN - 0.9999...到INT_MAX + 0.9999...]的转换对int是有效的。确保所使用的数学/值是准确的。当int具有比double精度更高的值位时,(double) INT_MAX可能会失去精度。
#include <limits.h>
#define INT_MAX_PLUS1  ((INT_MAX/2 + 1)*2.0)
#define INT_MIN_MINUS1 ((INT_MIN/2 - 1)*2.0)

#if INT_MIN == -INT_MAX
// Rare non-2's complement
if ((d < INT_MAX_PLUS1) && (d > INT_MIN_MINUS1)) {
#else
// 2's complement
if ((d < INT_MAX_PLUS1) && (d + INT_MIN > -1.0)) {
#endif

  i = (int) lround(d);

} else {
  fprintf(stderr, "Value %.17g not in int range [%d %d]\n", INT_MIN, INT_MAX);
}

通常情况下,当dnot-a-number时,d < INT_MAX_PLUS1为假。尽管false很常见,但C语言并不要求这种行为。或者可以这样表达:

#include <math.h>
if (!isnan(d) && (d < INT_MAX_PLUS1) && (d > INT_MIN_MINUS1)) {
...

-2
int b;
double a;
a=3669.0;
b=a;
printf("b=%d",b);

这段代码的输出结果应该是b=3669,你需要仔细核对。


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