微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

MariaDB POLYGON SELECT

如何解决MariaDB POLYGON SELECT

服务器:MariaDB 10.4.17

在小数点右侧插入 14 位数的多边形,然后选择相同的数据,返回小数点右侧具有 15 位数的多边形,这比实际存在的数据多,超出的精度是不正确的。

在小数点右侧插入 15 位数的 0 填充 polyGON,然后选择相同的数据,返回小数点右侧 15 位数的 polyGON,但是 SELECTed 数据的最后一位不正确并且不是用于右填充的 0。

因为表数据不正确,像 ST_Contains() 这样的各种 Geometry 函数会产生不正确的结果。这似乎是某种浮点类型的错误,但我不确定如何解决

有什么办法可以让 MariaDB 保存、使用和返回相同的数据吗?

示例:


INSERT INTO `Area` 
        (`Name`,`Coords`) 
VALUES ('Test ',GeomFromText('polyGON((
                    -76.123527198020080 43.010597920077250,-76.128263410842290 43.016193091211520,-76.130763247573610 43.033194256815040,-76.140676208063910 43.033514863935440,-76.13626333248750 43.008550330099250,-76.123527198020080 43.010597920077250))'));

SELECT Coords FROM `Area` WHERE `Name` = 'Test';

polyGON ((
                     -76.123527198020085 43.010597920077252,-76.128263410842294 43.01619309121152,-76.130763247573611 43.033194256815037,-76.140676208063908 43.033514863935437,-76.136263332487502 43.008550330099247,-76.123527198020085 43.010597920077252
         ))

编辑

根据@Michael-Entin 的说法,浮点错误一个死胡同,无法对我得到的错误的大小负责。

更新

问题是“我”。我不小心在其中一个查询中使用了 MBRContains() 而不是 ST_Contains()。

MBRContains 使用将包含多边形的“最小边界矩形”,而不是实际的 polyGON 坐标。

使用 MBRContains 导致该区域明显大于预期,并且似乎是处理错误,但事实并非如此。

ST_Contains() 速度较慢,但​​尊重所有 polyGON 边并产生正确的结果。

感谢@Michael-Entin 注意到浮点错误无法解释我遇到的错误的严重程度。这些信息为我指明了正确的方向。

解决方法

我认为你的精度已经达到了 64 位浮点的极限,你得到的实际上是 CPU 可表示的最接近的浮点值。

下面的代码不加任何修改地打印输入值,然后接下来的双浮点值以尽可能小的数量递减和递增:

int main() {
    const double f = -76.123527198020080;
    cout << setprecision(17) << f << endl
        << nextafter(f,-INFINITY) << endl
        << nextafter(f,INFINITY) << endl;
}

我得到的结果

-76.123527198020085
-76.123527198020099
-76.123527198020071

如您所见,-76.123527198020085 是与您的坐标 -76.123527198020080 最接近的值,其最接近的可能邻居是 -76.123527198020099(甚至更远),-76.1235207199 与 -76.1235207180 的方向也略有不同。

>

所以我认为没有任何方法可以保持您想要的精度。也不应该有保持这种精度的实际理由(差异小于一微米,即 1e-6 米)。

您应该注意的是,ST_Contains 究竟是如何不符合您的期望的。几何库通常以略高于坐标数值精度的公差距离进行捕捉,这在理想情况下应确保输入值的微小差异不会影响此类函数的结果。

,

大多数浮点硬件将采用基数 2。
如果我们尝试在基数 2 中分解 -76.128263410842290 的绝对值:

64 (2^6) + 8 (2^3) + 4 (2^2) + 0.125 (2^-3) + ...

不知何故,我们可以用 1001100.001 位序列记录以 2 为基数的这个数字...
运气不好,在基数 2 中,这个数字将需要这样的位的无限序列。
序列开始于:

1001100.001000001101010111011110111100101101011101001110111000...

但是浮点数的精度有限,IEEE 双精度中的有效数只有 53 位,包括小数分隔符之前的位。
这意味着最低有效位(最小精度的单位)代表 2^-46...

1001100.001000001101010111011110111100101101011101001110111000...
1001100.00100000110101011101111011110010110101110101

请注意,浮点值已四舍五入(到最接近的浮点数)。

让我们将 2^-46 乘以适当的 5 次幂 5^46/5^46:它是 5^46/10^46。
这意味着它的 DECIMAL 表示正好在 DECIMAL 点之后的 46 位结束,或者如果浮点有效数的尾随位为零(这里不是这种情况,尾随位为 1)则少一点。

因此,这些浮点数的小数部分可能有大约 46 位数字,甚至不是您认为的 14 位或 15 位。

如果我们把这个浮点值转回十进制,我们确实得到:

-76.12826341084229397893068380653858184814453125
-76.128263410842290

看到它确实比您在此处的初始输入略大,因为浮点数已四舍五入。

如果您要求在小数分隔符后打印 15 个小数位,您会得到一个四舍五入的结果。

-76.128263410842294

在这个浮点数中,最后一位 2^-46 是十进制值

0.0000000000000142108547152020037174224853515625

其中 142108547152020037174224853515625 是 5^46,你可以算一下。

直接浮点值在最后一位会有所不同(我们可以添加或减去它)

1001100.00100000110101011101111011110010110101110100
1001100.00100000110101011101111011110010110101110101
1001100.00100000110101011101111011110010110101110110

这意味着直接的浮点邻居大约是 +/- 1.42 10^-14 进一步...
这意味着你不能相信小数后的第 14 位数字,双精度没有这样的分辨率!
最近的浮点数有时会下降到指定输入的 7 10^-15 并不奇怪(由于舍入到最近的规则,分辨率减半)。
请记住,浮点精度是相对的,如果我们消耗小数分隔符的剩余位,我们会降低小数部分的精度(该点字面上是浮动的)。

这是科学家在使用浮点数之前应该掌握的非常基础的知识。
我希望这些示例作为一个非常有限的介绍有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。