C++ 如何信任从双精度到整数的转换?

C++ 如何信任从双精度到整数的转换?,c++,casting,division,double-precision,C++,Casting,Division,Double Precision,我一直在编写一些创建直方图的简单代码,发现以下代码: double value = 1.2; double bucketSize = 0.4; double bucketId = value / bucketSize; std::cout << "bucketId as double: " << bucketId << std::endl; std::cout << "bucketId as int: " << int(bucketI

我一直在编写一些创建直方图的简单代码,发现以下代码:

double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;

std::cout << "bucketId as double: " << bucketId << std::endl;
std::cout << "bucketId as int: " << int(bucketId) << std::endl;

这基本上破坏了我对计算机的信任;)在创建直方图的同时,查找
值的右侧
bucketId

我知道存在舍入错误等,但是否有解决该问题的一般解决方案

(以防万一)在转换到
int
之前,请不要建议在除法结果中添加
0.5
,因为在某些情况下它显然不太有效(例如
double value=3;double bucketSize=2;

提前感谢。

您可以使用
round()


我相信这总是需要0.5个数字,因此如果这对您的情况不好,它可能不是最佳解决方案

使用
std::lrund
。它返回与双倍数最接近的整数

#include<numeric>
double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;

std::cout << "bucketId as int: " << std::lround(bucketId) << std::endl;
#包括
双值=1.2;
双扣尺寸=0.4;
双bucketId=值/bucketSize;

std::cout在注释中,您表示希望得到结果的
整数部分
。嗯,不幸的是,
1.2/0.4
double
结果恰好是
2.99999999996
(在我的机器上。您可以使用
std::setprecision
查看
cout
的确切值),因此结果的整数部分是
2
。正如您所知,这是因为并非所有数字都可以用浮点数表示,而且浮点数操作会产生错误

取浮点数的整数部分与将浮点数与相等数进行比较处于同一级别;你不会得到一致的结果。如果您必须得到精确的结果,一般的解决方案是根本不使用浮点数,而是使用定点


与相等比较一样,您可以使用适当的epsilon值来解决这个问题。在这种情况下,在取整数部分之前,可以向结果中添加(如果为负数,则减去)一个非常小的浮点数。添加的数字必须大于该数字可能存在的最大错误,但小于必须支持的最小精度数字(因此,如果必须支持低至0.001精度,则9.999不会变为10)。计算出一个好的数字是相当困难的。

如果你想在它前面加0.25,返回下一个数字(比如
(double)1.75
(int)2
),使用
int(floor(buckedId+0.25))


问题是你想把它四舍五入到前一个数字的多少。

可能是固定的十进制近似值

(int)(value * 100)/(int)(bucketSize *100)
你会的

bucketId为double:3.000000000000000

bucketId as int:2


之所以会出现这种情况,是因为double的精度限制是15,您也可以尝试使用long double并改变精度。

我或多或少是基于您对其他人的一些评论。 要获得整数部分,解决方案是使用
modf
。但是
1.2/0.4
的整数部分很可能是
2
,而不是
3
0.4
不能用机器浮点表示(大多数 他们,至少),所以你被一些非常接近的东西分开了
0.4

真正的问题是你到底想要什么。如果你在看 使自由化(这样一个词是否存在)取决于
bucketSize
,那么正确的方法是使用 周围的缩放整数:

int value = 12;
int bucketSize = 4;
int bucketId = value / bucketSize;
然后:

std::cout << "bucketId as double: " << bucketId / 10.0 << std::endl;
std::cout << "bucketId as int: " << bucketId / 10 << std::endl;
这取决于你决定什么样的价值是合适的
yourEpsilonHere
;这取决于应用程序。(一次 我使用了这个技术,我们使用了
1E-9
。这并不意味着
不过,这对你来说是合适的。)

我建议你做一些类似的事情

double d = 1.4 / 0.4;
int whole = (int)d;
int nextWhole = whole + 1;

int result = whole;

if (fabs(d - nextWhole) < EPSILON) result = nextWhole;
double d=1.4/0.4;
整数=(整数)d;
int NEXTWHORE=整个+1;
int结果=整体;
如果(fabs(d-nextWhole)
(这适用于正数)


基本上,如果您的数字与下一个整数非常接近,以至于无关紧要,则此代码将使用下一个整数。

您希望3/2是什么?结果的整数部分,因此3表示1.2/0.4,1表示3/2。因此
std::round()
不会有帮助。“这基本上破坏了我对计算机的信任”--是的,浮点往往会有这种效果。首先,在小数点后20位打印1.2和0.4。你可能会有一些有趣的发现。下一步,阅读添加
0.5
可能会以微妙的方式失败,请参见中间的示例my。问题是不需要“最近的”-我需要数字的整数部分。对于结果为1.6的
lround
将给我2而不是预期的1。根据您的描述,您需要最接近的。否则,您应该使用
(int)(1.2/0.4)
,因为这正好提取数字的整数部分。。。在这台机器上是
2
,在另一台机器上是
3
。要获得整数部分,请使用
modf
。问题是你并不真正想要整数部分,因为
1.2/0.4
的整数部分是
2
,至少在你的机器上是这样。我的“结果”是指数学结果而不是机器除法的结果:)@Moomin是的,我在我的回答中假设了。谢谢你的回答。我发现在转换为
int
之前转换为
float
在这里提供了一些帮助(我的情况),但也不是一个通用的解决方案。@Moomin,这将取决于实际值。将结果转换为
float
可能会有所帮助,但如果使用其他值,则可能会产生“错误”的结果,在不使用强制转换的情况下可以获得正确的结果。对于初学者:
double bucketSize=0.4,然后以非常高的精度输出
bucketSize
。(尽管您的实现可能不支持足够大的精度,因此无法应用
int value = 12;
int bucketSize = 4;
int bucketId = value / bucketSize;
std::cout << "bucketId as double: " << bucketId / 10.0 << std::endl;
std::cout << "bucketId as int: " << bucketId / 10 << std::endl;
int
asInt( double d )
{
    double results;
    double frac = modf( d, &results );
    if ( frac > 1.0 - yourEpsilonHere ) {
        results += 1.0;
    }
    return results;
}
double d = 1.4 / 0.4;
int whole = (int)d;
int nextWhole = whole + 1;

int result = whole;

if (fabs(d - nextWhole) < EPSILON) result = nextWhole;