代码之家  ›  专栏  ›  技术社区  ›  DTs

在MySQL中存储CLLocationDegrees时丢失精度

  •  1
  • DTs  · 技术社区  · 12 年前

    我正在开发一个可以存储 地名标注 由iOS设备指定的MySQL后端。当设备保存新的 地标 ,它被插入到DB中,并且新生成的ID被发送回客户端。如果另一个设备试图保存相同的 地标 ,服务器需要弄清楚它已经存在于DB中,并且不需要INSERTing并生成新的ID,而是需要将该Placemark的现有ID发送回客户端。

    2. 地名标注 如果它们具有相同的:

    • 名称
    • 纬度
    • 经度

    因此,服务器试图通过发出

    SELECT id FROM占位符WHERE name=x,latitude=y,经度=x

    • 在iOS中, latitude longitude 以度为单位,属于类型 CLLocationDegrees 据报告 <CoreLocation>
    • 在MySQL中, 纬度 经度 定义为 DOUBLE


    问题是:
    存储坐标时会丢失精度,例如:
    latidude 属于 50.09529561485427 存储为 50.0952956148543
    经度 属于 -122.9893130423316 存储为 -122.989313042332

    问题是:
    我可以在MySQL中使用不同的数据类型来完整地存储每个坐标吗?

    笔记:

    • 我无法将坐标存储为 TEXT VARCHAR 因为我需要能够说 SELECT placemarks coordinates BETWEEN x AND y ,或执行其他算术选择
    • 我不能使用MySQL的空间扩展,因为可移植性是一个问题,而且据我所知,空间数据类型用作键时非常慢(我的用例需要按坐标进行SELECT)

    感谢您的帮助

    2 回复  |  直到 12 年前
        1
  •  3
  •   DTs    12 年前

    实际上,进一步的测试揭示了以下奇怪的结果:当DB驱动程序将数据绑定到查询语句时,精度会丢失,并且 之前 数据实际上被插入到DB中。

    这有以下有趣的结果:即使我将关键字绑定到后续的搜索查询,精度似乎也会以相同的方式丢失,因此几乎“意外”地返回了正确的结果。也就是说,键(坐标)存储不正确,随后的选择也不正确地转换查询键,但以匹配的方式转换,因此找到了记录。考虑到由于精度损失而导致的转换总是在同一平台(服务器)上执行,保持原样可能是安全的??或者可能不是,也许未来的迁移操作(即,由于缩放)会使舍入误差变得明显。

    Yawar的指针让我看到了MySQL的手册,其中解释了浮点数字可以存储为DECIMAL,而不会丢失精度:

    精确值数字文本有整数部分或小数部分,或两者兼而有之。它们可能是有符号的 http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html

    假设现有的解决方案似乎有效,那么问题就变成了,保留DB数据类型更好吗 DOUBLE ,或切换到 DECIMAL ?

    存储:
    Latitude 范围从 -90 to +90 ,而iOS似乎使用的精度为 14 decimals
    Longitude 范围从 -180 to +180 ,这使得精度为 13 decimals 在iOS上

    这意味着我们需要:
    纬度 : DECIMAL(16,14) ,其占用的存储空间小于8字节(每包9d占用4字节)
    经度 : DECIMAL(16,13) ,还需要少于8字节的存储空间

    相反,类型为的列 双重 需要8字节的存储空间。

    速度(算术):
    浮点运算几乎总是快得多,因为它是由硬件本地支持的

    速度(搜索):
    我相信指数 十进制的 vs的索引 双重 具有非常相似的运行时。

    结论:
    切换到 十进制的 消除了精度误差,还降低了存储需求,在这种用例中,算术性能的损失可以忽略不计。

        2
  •  0
  •   progrmr    12 年前

    与其将纬度和经度存储为双倍,不如乘以1000000,转换为整数并存储。当然,如果你需要在你的应用程序中读回来,可以除以100000.0。

    这将为您提供足够的占位符精度(0.000001),并将消除浮点转换和精度问题,并且占用的空间略小。我在自己的数据中使用这个(它是二进制的,而不是SQL),并称之为MicroDegrees。