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;