[英]Converting latitude and longitude to float in C
I'm trying to convert Lat and Lon coordinates to 4 decimal place float. 我正在尝试将纬度和经度坐标转换为小数点后4位浮点数。 I have 'working' code, but it's just a bit off like %50 of the time.
我有“有效”的代码,但与%50的时间相比有点差。 Can anyone help me or have a better design to make it more exact?
谁能帮我或者有一个更好的设计使其更精确?
float sexag2decimal(char * deg){
char *sdeg = malloc(sizeof(char)*3);
char *smin = malloc(sizeof(char)*3);
char *ssec = malloc(sizeof(char)*3);
char *ssec2 = malloc(sizeof(char)*5);
int size = strlen(deg);
int m = 0;
for(m = 0; m < size-1; m++){
if(deg[m] >= 65 && deg[m] <= 122){
return 0;
}
}
float converted = 0;
if(deg[0] == '0'){
strcpy(&sdeg[0], °[1]);
strcpy(&sdeg[1], °[2]);
strcpy(&smin[0], °[4]);
strcpy(&smin[1], °[5]);
strcpy(&ssec[0], °[7]);
strcpy(&ssec[1], °[8]);
strcpy(&ssec2[0], °[10]);
strcpy(&ssec2[1], °[11]);
strcpy(&ssec2[2], °[12]);
strcpy(&ssec2[3], °[13]);
}else{
if(deg[14] == 'W')
return 0;
strcpy(&sdeg[0], °[0]);
strcpy(&sdeg[1], °[1]);
strcpy(&smin[0], °[3]);
strcpy(&smin[1], °[4]);
strcpy(&ssec[0], °[6]);
strcpy(&ssec[1], °[7]);
strcpy(&ssec2[0], °[9]);
strcpy(&ssec2[1], °[10]);
strcpy(&ssec2[2], °[11]);
strcpy(&ssec2[3], °[12]);
}
sdeg[2] = '\0';
smin[2] = '\0';
ssec[2] = '\0';
ssec2[4] = '\0';
converted = atoi(sdeg) + ((float)atoi(smin)/60.0) + (((float)atoi(ssec)+((float)atoi(ssec2))/10000)/3600.0);
free(sdeg);
free(smin);
free(ssec);
free(ssec2);
return converted;
}
Thanks! 谢谢!
Input: 输入:
30-25-30.7140N, 086-53-37.8590W 30-25-30.7140N,086-53-37.8590W
29-57-33.3000N,081-20-23.0000W 29-57-33.3000N,081-20-23.0000W
My output: 我的输出:
30.4252,-86.8939 30.4252,-86.8939
29.9592,-81.3397 29.9592,-81.3397
Correct output: 正确的输出:
30.4252,-86.8938 30.4252,-86.8938
29.9593,-81.3397 29.9593,-81.3397
Thanks! 谢谢!
OP's code suffers from attempting to write outside allocated buffers. OP的代码遭受尝试在外部分配的缓冲区中写入的麻烦。 @cyclaminist
@cyclaminist
float sexag2decimal(char * deg){
char *sdeg = malloc(sizeof(char)*3); // Too small for the entire string
...
strcpy(&sdeg[0], °[0]); // UB: attempts to copy the _entire_ string to `deg`
Certainly OP meant to code something like 当然,OP意味着要编写类似
// strcpy(&sdeg[0], °[0]);
sdeg[0] = deg[0]; // Copy 1 char
it's just a bit off like %50 of the time
大概有百分之五十的时间
Given OP's code survived the above UB, the observed output differs from expectation in part due to the result of accumulated roundings in the calculation: 给定OP的代码在上述UB中仍然有效,则观察到的输出与预期有所不同,部分原因是计算中累积了舍入的结果:
converted = atoi(sdeg) + ((float)atoi(smin)/60.0) +
(((float)atoi(ssec)+((float)atoi(ssec2))/10000)/3600.0);
This incurs rounding in (float)atoi(smin)/60.0
, (float)atoi(ssec2))/10000)
, (float)atoi(ssec2))/10000)/3600.0)
and each of the 3 additions. 这会导致四舍五入为
(float)atoi(smin)/60.0
, (float)atoi(ssec2))/10000)
, (float)atoi(ssec2))/10000)/3600.0)
和3个添加项中的每一个。
Using higher precision math, like double
rather than float
, will dramatically reduce rounding effects, but not eliminate them . 使用诸如
double
而不是float
高精度数学将大大减少舍入效果, 但并不能消除它们 。
To improve accuracy, perform the calculation with no rounding with integer math. 为了提高精度,请使用整数数学不进行四舍五入来执行计算。 The expected values of angle, in 10,000th of a second, needs about 36-bit math.
角度的期望值(以10,000秒为单位)大约需要36位数学运算。
long seconds = ((atoi(sdeg) * 60) + atoi(smin)) * 60 + atoi(ssec);
long myriad_seconds = (seconds * 10000) + atoi(ssec2);
... to 4 decimal place float
...至小数点后四位
This goal is somewhat a contradiction. 这个目标有点矛盾。
float
is very commonly employs a binary encoding and so does not produce a decimal . float
非常通常采用二进制编码,因此不会产生十进制数 。 It does sound like OP want to print to 4 decimal places some float
. 这听起来像OP要打印到小数点后4位一些
float
。 To do so, code suffers additional roundings. 这样做会使代码遭受更多的舍入。
The conversion of the exact myriad_seconds
to a float
incurs a rounding error. 精确的
myriad_seconds
转换为float
会导致舍入错误。
// "29-57-33.3000N"
// myriad_seconds = 1078533000
// With infinite math, the quotient is 29.95925
float converted = myriad_seconds/(60.0f * 60 * 10000);
// The "best" `float` is just less than 29.95925
// converted = 29.959249777...
The conversion of a float
to 4 decimal place text incurs another rounding error. 将
float
转换为小数点后4位文字会导致另一个舍入错误。
printf("%.4f\n", converted);
// "29.9592"
If this is OP's goal, performing far fewer roundings like this code with greatly improve yet not eliminate the "once-in-a-while" off by one bit. 如果这是OP的目标,则执行这样的代码所需的舍入要少得多,从而大大提高了性能,但一点儿也没有消除“一次”。
The originator of this task has cleverly chosen final values that are near xxx.xxxx5...
. 此任务的创建者已经巧妙地选择了
xxx.xxxx5...
附近的最终值。 These values are sensitive to the computational path in being "just a bit off". 这些值对“略微偏离”的计算路径很敏感。
To counter this craftiness, consider instead of returning a float
, return an integer of the number of 10,000th of a degree. 为了解决这种诡计,请考虑不返回
float
,而是返回一个整数,该整数是度的10,000分之一。 With error/rounding free myriad_seconds
and controlled rounding to myriad_degrees
, code will always achieve the expected answer - at a cost of more complex source code. 使用免费的错误/舍入
myriad_seconds
和将舍入到myriad_degrees
舍入,代码将始终达到预期的答案-代价是更复杂的源代码。
// Add signed half of 3600 to effect rounding (half way away from zero) before division.
long myriad_degrees = (myriad_seconds + (myriad_seconds < 0 ? -1 : 1) * 3600 / 2) / 3600;
printf("%s%ld.%04ld\n", myriad_degrees < 0 ? "-" : "",
labs(myriad_degrees) / 10000, labs(myriad_degrees) % 10000);
// Output
29.9593
(copied out of own comments) (摘自自己的评论)
First, the great part of time this function wastes seems to be in malloc
. 首先,此函数浪费的大部分时间似乎在
malloc
。 You have no reason to use dynamic heap at all: just allocate character arrays like 您根本没有理由使用动态堆:只需分配字符数组,例如
char sdeg[3];
Moreover, each return from function not from the last line causes 4 memory leaks ( free
is not called). 此外,每次从函数返回而不是从最后一行返回都会导致4次内存泄漏(不调用
free
)。 Again, you don't need malloc
at all. 同样,您根本不需要
malloc
。
Second, strcpy
is inappropriate here: just copy the character. 其次,
strcpy
在这里不合适:仅复制字符。 strcpy
requires NUL
-terminated string and copies full length of source. strcpy
需要NUL
终止的字符串,并复制源的完整长度。 That's not the thing you need here. 那不是您在这里需要的东西。
Third: if you check input for saneness, excluding character range 65...122 isn't good idea. 第三:如果您检查输入是否合理,则排除字符范围65 ... 122并不是一个好主意。 Particularly, you reject the case it's 'W', despite later you expect 'W' as a possible input character.
特别是,您拒绝使用“ W”的情况,尽管以后您希望将“ W”作为可能的输入字符。
In general, I suggest using sscanf. 通常,我建议使用sscanf。 With it, you can write code like:
有了它,您可以编写如下代码:
if (sscanf(deg, "%d-%d-%d.%d", &n1, &n2, &n3, &n4) == 4) {
return some_formula_on_n1...n4;
}
Finally, and the main issue: float
accuracy is definitely not enough for your expectations. 最后,还有一个主要问题:
float
精度绝对不能满足您的期望。 1/10000 of a second (which in turn is 1/3600 of a degree) is 1/6,480,000,000 (of a full degree range 0...180) that is much smaller than float
relative error (1/8,388,608). 1/10000秒(即1/3600度)是1 / 6,480,000,000(全度范围0 ... 180),比
float
相对误差(1 / 8,388,608)小得多。 Switch to double
in all values. 切换为所有值的
double
。 Also, request to print at least 10 significant digits. 另外,要求打印至少10个有效数字。 6 default ones are too small.
6个默认值太小。
You could also consider the method described by @chux for only integer maths. 您也可以考虑仅对整数数学使用@chux描述的方法。 Unlike binary floating, it's definitely free of rounding effects and hard-to-prove accuracy, despite more complex (and cumbersome).
与二进制浮动不同,尽管更加复杂(而且麻烦),但它绝对没有舍入效果和难以证明的准确性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.