给定数字在哪个细分中?

时间:2016-06-29 10:15:04

标签: c++ algorithm floating-point

假设n(整数)个连续的长度为l的段(浮点)。那就是:

Segment 0 = [0, l)
Segment 1 = [l, 2*l)
Segment 2 = [2*l, 3*l)
... 
Segment (n-1) = [(n-1)*l, n*l) 

给定一个数字x(浮点数)我想确定它所在的段的id。

我的第一个想法如下:

int segmentId = (int) floor(x/l);

无论如何,这有时不起作用。例如,考虑

double l = 1.1;
double x = 5.5;
int segmentId = (int) floor(x/l); //returns 5


double l = 1.1;
double x = 6.6;
int segmentId = (int) floor(x/l); //returns 5!!!

当然,由于有限的算术,这不能很好地工作。 为了实现强大的实现,可能需要一些额外的检查,但我真的不知道如何继续进行。

问题是:您如何解决问题“给定数字在哪个细分中?”

5 个答案:

答案 0 :(得分:7)

您的问题是,1.16.6都不能完全以二进制浮点表示。所以当你输入

double l = 1.1;
double x = 6.6;

您在lx中存储了2个数字,这些数字与1.16.6略有不同。之后,int segmentId = (int) floor(x/l);为那些略有不同的数字确定正确的细分,但不是原始数字。

您可以使用十进制浮点数据类型而不是二进制来解决此问题。您可以检查C++ decimal data typesExact decimal datatype for C++?是否有库,或者自己实现小数数据类型。

但问题仍然存在于数字中,这些数字在有限的十进制浮点数中无法表示,例如1/3(循环分数),sqrt(2)(无理),pi(超越) )等等。

答案 1 :(得分:2)

如果您不特意想要O(1)答案,您可以通过对片段进行二元搜索来获取O(logn)答案。

答案 2 :(得分:1)

您的解决方案需要什么精度?对于给定的段,边缘值总是存在问题,因为它们很可能是不可代表的。 我认为在这种情况下添加一个非常小的epsilon可能会有所帮助。但是在其他情况下它可能会失败。

答案 3 :(得分:1)

  

你将如何解决问题“给定数字在哪个部分?”

您应该将数字除以段长度,然后将小数部分截断。像这样:

int segmentId = (int) floor(x/l);

似乎你已经想到了这一点。

  

当然,由于算术有限,这种方法效果不佳。

如果6.6 / 1.1的结果恰好是5.9999999999999991118215802998747676610946655273438,则5实际上是结果的正确段。

如果你希望6.6 / 1.1正好是6,那么你的问题是有限精度除法,它不能做你想要的,并且浮点数的有限精度表示没有确切的代表性数字。细分本身运作得很好。

  

我真的不知道如何继续进一步

要么不使用有限精度浮点(使用固定或任意精度),要么不要求计算结果准确。

答案 4 :(得分:1)

分割后再次检查细分。

bool inSegment(double x, double l, double segment)
{
    return (x >= l*(segment-1)) && (x < l*segment);
}

int segmentId;
double segment = floor(x/l);

if (inSegment(x, l, segment-1))
    segmentId = segment - 1;
else if (inSegment(x, l, segment))
    segmentId = segment;
else if (inSegment(x, l, segment+1))
    segmentId = segment + 1;
else
    printf("Something wrong happened\n");

如果值足够接近上面的整数,则使用epsilon并向上舍入值。