假设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!!!
当然,由于有限的算术,这不能很好地工作。 为了实现强大的实现,可能需要一些额外的检查,但我真的不知道如何继续进行。
问题是:您如何解决问题“给定数字在哪个细分中?”
答案 0 :(得分:7)
您的问题是,1.1
和6.6
都不能完全以二进制浮点表示。所以当你输入
double l = 1.1;
double x = 6.6;
您在l
和x
中存储了2个数字,这些数字与1.1
和6.6
略有不同。之后,int segmentId = (int) floor(x/l);
为那些略有不同的数字确定正确的细分,但不是原始数字。
您可以使用十进制浮点数据类型而不是二进制来解决此问题。您可以检查C++ decimal data types和Exact 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并向上舍入值。