简体   繁体   English

将经度和纬度转换为C语言

[英]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], &deg[1]);
        strcpy(&sdeg[1], &deg[2]);
        strcpy(&smin[0], &deg[4]);
        strcpy(&smin[1], &deg[5]);
        strcpy(&ssec[0], &deg[7]);
        strcpy(&ssec[1], &deg[8]);
        strcpy(&ssec2[0], &deg[10]);
        strcpy(&ssec2[1], &deg[11]);
        strcpy(&ssec2[2], &deg[12]);
        strcpy(&ssec2[3], &deg[13]);
    }else{
        if(deg[14] == 'W')
            return 0;
        strcpy(&sdeg[0], &deg[0]);
        strcpy(&sdeg[1], &deg[1]);
        strcpy(&smin[0], &deg[3]);
        strcpy(&smin[1], &deg[4]);
        strcpy(&ssec[0], &deg[6]);
        strcpy(&ssec[1], &deg[7]);
        strcpy(&ssec2[0], &deg[9]);
        strcpy(&ssec2[1], &deg[10]);
        strcpy(&ssec2[2], &deg[11]);
        strcpy(&ssec2[3], &deg[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], &deg[0]); // UB: attempts to copy the _entire_ string to `deg`

Certainly OP meant to code something like 当然,OP意味着要编写类似

    // strcpy(&sdeg[0], &deg[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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM