我怎么能相信从double到integer的强制转换

How can I trust casting from double to integer?

本文关键字:转换 integer 怎么能 相信 double      更新时间:2023-10-16

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

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 as double: 3
bucketId as int: 2

这基本上破坏了我对电脑的信任;)当在创建直方图的同时为CCD_ 2寻找右CCD_。

我知道有舍入误差等,但有没有通用的解决方案?

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

提前谢谢。

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

真正的问题是你到底想要什么。如果你在看根据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;

否则,如果您想将值保持为双倍,您将必须决定对于到CCD_ 13的转换有多接近,然后使用您自己的功能:

int
asInt( double d )
{
    double results;
    double frac = modf( d, &results );
    if ( frac > 1.0 - yourEpsilonHere ) {
        results += 1.0;
    }
    return results;
}

由您决定什么价值适合yourEpsilonHere;这取决于应用程序。(一次我用了这个技术,我们用了1E-9。这并不意味着不过,这对你来说很合适。)

在注释中,您说您想要Integer part of the result。不幸的是,1.2 / 0.4double结果恰好是2.9999999999999996(在我的机器上。你可以通过使用value1看到cout的确切值),因此结果的整数部分是2。正如您所知,这是因为并非所有数字都可以用浮点数字表示,而且浮点运算会产生错误。

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

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

使用std::lround。它返回与您的双位数最接近的整数。

#include<numeric>
double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;
std::cout << "bucketId as int: " << std::lround(bucketId) << std::endl;

请注意,3.0/2.0仍然可能导致意外的结果,这取决于结果是1.4999998还是1.5000001

也许是固定的十进制近似?

(int)(value * 100)/(int)(bucketSize *100)

如果要在0.25之前返回下一个数字(如(double)1.75(int)2),请使用int(floor(buckedId+0.25))

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

#include<iostream>
#include <limits>
int main()
{
    double value = 1.200000000000000;
    double bucketSize = 0.4000000000000000;
    double bucketId = value / bucketSize;
    std::cout.precision(16);
    std::cout << "bucketId as double: " <<  std::fixed << bucketId << std::endl;
    std::cout << "bucketId as int: " << int(bucketId) << std::endl;
    return 1;
}

在你的系统上试试这个——你想要

bucketId为双:2.9999999999999996

bucketId为int:2

与相比

std::cout.precision(15);

你会

bucketId为双:3.000000000000000

bucketId为int:2

之所以会发生这种情况,是因为double的精度限制是15,你也可以尝试设计长double并改变精度。

我建议一些类似的东西

double d = 1.4 / 0.4;
int whole = (int)d;
int nextWhole = whole + 1;
int result = whole;
if (fabs(d - nextWhole) < EPSILON) result = nextWhole;

(这适用于正数)

基本上,如果你的数字非常接近下一个整数,那么这段代码将使用下一个整型。

您可以使用round()

http://www.cplusplus.com/reference/cmath/round/

我相信这总是让0.5个数字远离零,所以如果这对你的情况不好,它可能不是最佳解决方案