1. 背景
当你在使用导航、打车、定位等等场景下,一定会有形或者无形的使用位置服务,位置服务的基础功能功能就是向你提供位置信息,而经纬度是位置信息的主要信息,一般情况可以简单的认为位置信息就是经纬度信息。经纬度使用小数进行表示,小数在计算机内部以浮点表示,在IEEE754浮点原理 - 计算机存储小数的误差有多少-CSDN博客文章浏览阅读1k次,点赞11次,收藏21次。本质上计算机内部使用浮点就是二进制小数的科学计算法表示,与相同长度的整数(如都为32位长度)相比能表示更大的范围,但是浮点并不是全范围内等精度,它会随着要表示的数的绝对值变大而精度逐渐变小。https://blog.csdn.net/bitslink/article/details/139078980 已知,使用浮点可能存在误差,而且这个误差会随这个数增加而增大。那经纬度应该使用单精度浮点float还是双精度浮点doubble类型呢?首先应该清楚什么是经纬度?
2. 什么是经纬度
经纬度是一套球面的坐标系统,纬度是指球面任意一点与球心连线与赤道面的夹角,范围在-90°~90°,赤道面以北成为北纬(一般使用正数表示),赤道面以南成为南纬(一般使用负数表示)。经度是指地球面上一点与两极的连线与0度经线所在平面的夹角,范围-180~+180(负东经,正西经)。经纬度如下图在球面上如下图所示:
3. 经纬度使用Float(单精度)的误差
经纬度浮点数的最大范围在-180°~+180°,参考IEEE754浮点原理 - 计算机存储小数的误差有多少-CSDN博客对该范围内浮点小数进行分辨率分析,分析源码如下:
#include "stdio.h"
#include "stdint.h"
#define NUM 19
int main()
{
float ff[NUM];
float yy[NUM];
float dd[NUM];
uint32_t *p;
for(int i=0;i<NUM;i++)
{
if(0 == i)
{
ff[i] = 0.1;
yy[i] = ff[0];
p = (uint32_t *)&yy[i];
*p += 1;
}
else
{
ff[i] = ff[i-1]+10;
yy[i] = ff[i];
p = (uint32_t *)&yy[i];
*p += 1;
}
dd[i] = yy[i] - ff[i];
printf("%0.2f\t %0.9f\n",ff[i],dd[i]);
}
printf("x=[");
for(int i=0;i<NUM;i++)
{
printf("%0.2f,",ff[i]);
}
printf("]\n");
printf("y=[");
for(int i=0;i<NUM;i++)
{
printf("%0.9f,",dd[i]);
}
printf("]");
}
浮点分析源码的运行结果如下:
0.10 0.000000007
5.10 0.000000477
10.10 0.000000954
15.10 0.000000954
20.10 0.000001907
25.10 0.000001907
30.10 0.000001907
35.10 0.000003815
40.10 0.000003815
45.10 0.000003815
50.10 0.000003815
55.10 0.000003815
60.10 0.000003815
65.10 0.000007629
70.10 0.000007629
75.10 0.000007629
80.10 0.000007629
85.10 0.000007629
90.10 0.000007629
95.10 0.000007629
100.10 0.000007629
105.10 0.000007629
110.10 0.000007629
115.10 0.000007629
120.10 0.000007629
125.10 0.000007629
130.10 0.000015259
135.10 0.000015259
140.10 0.000015259
145.10 0.000015259
150.10 0.000015259
155.10 0.000015259
160.10 0.000015259
165.10 0.000015259
170.10 0.000015259
175.10 0.000015259
180.10 0.000015259
185.10 0.000015259
将运行结果,绘图后如下:
上图中浮点小数在130~180时分辨率最低,此时float分辨率仅为(十万分子1.5),由分辨率引入的误差也为,弧长计算公式 。
根据弧长公式以及地球半径约为6371km,将上面曲线图纵轴转换成弧长的分辨率后,结果如下:
0.10 0.000828467
5.10 0.053021874
10.10 0.106043749
15.10 0.106043749
20.10 0.212087497
25.10 0.212087497
30.10 0.212087497
35.10 0.424174994
40.10 0.424174994
45.10 0.424174994
50.10 0.424174994
55.10 0.424174994
60.10 0.424174994
65.10 0.848349988
70.10 0.848349988
75.10 0.848349988
80.10 0.848349988
85.10 0.848349988
90.10 0.848349988
95.10 0.848349988
100.10 0.848349988
105.10 0.848349988
110.10 0.848349988
115.10 0.848349988
120.10 0.848349988
125.10 0.848349988
130.10 1.696699977
135.10 1.696699977
140.10 1.696699977
145.10 1.696699977
150.10 1.696699977
155.10 1.696699977
160.10 1.696699977
165.10 1.696699977
170.10 1.696699977
175.10 1.696699977
180.10 1.696699977
185.10 1.696699977
经纬度在180°内对应弧长最低分辨率约为1.7米,而由分辨率导致的误差等于分辨率,最大存储误差为1.7米。若使用float对经纬度进行存储传输,那么由于采用了float单精度浮点会存在一定误差,这个误差最大值小于1.5米。对于中国经纬度范围为73.33~135.05,3.51~53.33,采用单精度浮点flaot最大的存储误差为1.7米,最小的存储误差为0.85米。
4. 经纬度使用double(双精度)的误差
与float类似首先对0~180内的浮点数进行进度分析,分析C语言源码如下:
#include "stdio.h"
#include "stdint.h"
#include "math.h"
#define NUM 40
int main()
{
double ffd[NUM];
double yyd[NUM];
double ddd[NUM];
uint64_t *pd;
for(int i=0;i<NUM;i++)
{
if(0 == i)
{
ffd[i] = 0.1;
yyd[i] = ffd[0];
pd = (uint64_t *)&yyd[i];
*pd += 1;
}
else
{
ffd[i] = ffd[i-1]+5;
yyd[i] = ffd[i];
pd = (uint64_t *)&yyd[i];
*pd += 1;
}
ddd[i] = (yyd[i] - ffd[i])*M_PI*6371000/180.0;
printf("%0.2f\t %0.17lf\n",ffd[i],ddd[i]);
}
printf("x=[");
for(int i=0;i<NUM;i++)
{
printf("%0.2lf,",ffd[i]);
}
printf("]\n");
printf("y=[");
for(int i=0;i<NUM;i++)
{
printf("%0.17lf,",ddd[i]);
}
printf("]");
}
运行结果如下:
0.10 0.00000000000154314
5.10 0.00000000009876093
10.10 0.00000000019752187
15.10 0.00000000019752187
20.10 0.00000000039504374
25.10 0.00000000039504374
30.10 0.00000000039504374
35.10 0.00000000079008747
40.10 0.00000000079008747
45.10 0.00000000079008747
50.10 0.00000000079008747
55.10 0.00000000079008747
60.10 0.00000000079008747
65.10 0.00000000158017495
70.10 0.00000000158017495
75.10 0.00000000158017495
80.10 0.00000000158017495
85.10 0.00000000158017495
90.10 0.00000000158017495
95.10 0.00000000158017495
100.10 0.00000000158017495
105.10 0.00000000158017495
110.10 0.00000000158017495
115.10 0.00000000158017495
120.10 0.00000000158017495
125.10 0.00000000158017495
130.10 0.00000000316034990
135.10 0.00000000316034990
140.10 0.00000000316034990
145.10 0.00000000316034990
150.10 0.00000000316034990
155.10 0.00000000316034990
160.10 0.00000000316034990
165.10 0.00000000316034990
170.10 0.00000000316034990
175.10 0.00000000316034990
180.10 0.00000000316034990
185.10 0.00000000316034990
将结果绘制曲线图,如下:
根据弧长公式:,其中r为地球半径约为6371米、θ为角度,然后将上图的纵轴转换成弧长,如下图所示:
使用double类型后,分辨率为3.16*10-9米(3.16纳米),由分辨率导致的存储误差等于分辨率除2,最大误差约为1.58纳米。
5. 总结
对于经纬度使用单精度float存储传输,分辨率误差随所在地的经纬度增加而增加,当经纬度大于130°后存储误最大的存储误差约为1.7米。对于普通GNSS测量误差一般10米数量级,此时使用单精度float后对总的误差影响不大;但使用了双频GNSS或者RTK GSNSS后测量,前者测量误差在2~3米,否则测量误差约为10cm数量级,这时若仍然使用单精度float存储,此时可能存在存储误差要大于测量误差,此次可以使用双精度浮点进行存储,虽然这可能带来存储空间的翻倍。