2010-10-25 27 views
4

Je recherche le meilleur code C ou C++ pour encoder et décoder les valeurs de latitude et de longitude décimales de/à double/char. Je préférerais le code convertir de double en char [] et vice-versa plutôt que les chaînes C++.Quelle est la classe C++ la plus rapide ou la bibliothèque C pour convertir la latitude et la longitude des degrés décimaux en chaîne et en arrière

Si vous avez un extrait de code, ce serait génial aussi.

Pour clarifier: J'ai besoin de convertir à partir d'une chaîne de degrés/minutes/secondes pour doubler et revenir à la chaîne. J'ai 300 millions d'enregistrements, donc la vitesse est une grande préoccupation.

Voir: http://en.wikipedia.org/wiki/Geographic_coordinate_conversion

+1

Donc, vous écrivez C ou C++? Pourquoi doit-il être "le plus rapide"? Pourquoi pas le plus propre ou le plus élégant, ou le plus direct, ou le plus flexible? (Qui ne sont pas mutuellement exclusifs du tout.) Pourquoi éviter 'std :: string'? Pourquoi même mentionner C lorsque vous utilisez probablement C++? Pourquoi êtes-vous concentré sur la vitesse au lieu de la correction? – GManNickG

+0

Wow ... peut-être sa principale préoccupation est la vitesse? Avez-vous déjà entendu parler de l'information cachée/encapsulation? Peu importe comment la fonction est implémentée, tant que cela fonctionne. –

+0

Et quel est le problème du tout? Conversion entre C-string et double en fonction du format de la valeur pour la latitude et la longitude, ou de la manipulation de différents formats, ou du calcul des valeurs de latitude et de longitude concernant d'autres espaces de valeurs, ou ...? – Flinsch

Répondre

4

En travaillant avec l'OP (amanda) par e-mail, nous avons développé une fonction rapide basée sur une grande déclaration de casse.

Les rapports d'amanda qui s'exécute quelque part autour de 15x plus vite que le code qu'ils avaient utilisé. Considérant que cela dépasse 300 millions d'enregistrements, cela devrait représenter un gain de temps considérable.

J'ai trouvé le problème très intéressant.

Voici le code:

/* WARNING: These values are very important, as used under the "default" case. */ 
#define INT_PART 3 
#define DEC_PART 2 

double Str2LatLong(char* coord) 
//double LLStr::Str2LL(char* coord) 
{ 
    int sign = +1; 
    double val; 

    int i = 0; /* an index into coord, the text-input string, indicating the character currently being parsed */ 

    int p[9] = {0,0,1, /* degrees */ 
       0,0,1, /* minutes */ 
       0,0,1 /* seconds */ 
       }; 
    int* ptr = p; /* p starts at Degrees. 
         It will advance to the Decimal part when a decimal-point is encountered, 
         and advance to minutes & seconds when a separator is encountered */ 
    int flag = INT_PART; /* Flips back and forth from INT_PART and DEC_PART */ 

    while(1) 
    { 
     switch (coord[i]) 
     { 
      /* Any digit contributes to either degrees,minutes, or seconds */ 
      case '0': 
      case '1': 
      case '2': 
      case '3': 
      case '4': 
      case '5': 
      case '6': 
      case '7': 
      case '8': 
      case '9': 
       *ptr = 10* (*ptr) + (coord[i] - '0'); 
       if (flag == DEC_PART) /* it'd be nice if I could find a clever way to avoid this test */ 
       { 
        ptr[1] *= 10; 
       } 
       break; 

      case '.':  /* A decimal point implies ptr is on an integer-part; advance to decimal part */ 
       flag = DEC_PART; /* after encountering a decimal point, we are now processing the Decimal Part */ 
       ptr++; /* ptr[0] is now the Decimal piece; ptr[1] is the Denominator piece (powers of 10) */ 
       break; 

      /* A Null terminator triggers return (no break necessary) */ 
      case '\0': 
       val = p[0]*3600 + p[3]*60 + p[6];    /* All Integer math */ 
       if (p[1]) val += ((double)p[1]/p[2]) * 3600; /* Floating-point operations only if needed */ 
       if (p[4]) val += ((double)p[4]/p[5]) * 60; /* (ditto) */ 
       if (p[7]) val += ((double)p[7]/p[8]);   /* (ditto) */ 
       return sign * val/3600.0;     /* Only one floating-point division! */ 

      case 'W': 
      case 'S': 
       sign = -1; 
       break; 

      /* Any other symbol is a separator, and moves ptr from degrees to minutes, or minutes to seconds */ 
      default: 
       /* Note, by setting DEC_PART=2 and INT_PART=3, I avoid an if-test. (testing and branching is slow) */ 
       ptr += flag; 
       flag = INT_PART; /* reset to Integer part, we're now starting a new "piece" (degrees, min, or sec). */ 
     } 
     i++; 
    } 

    return -1.0; /* Should never reach here! */ 
} 
+0

C'était une amélioration impressionnante de la vitesse par rapport à tout ce que nous avions essayé. Comme l'a dit abelenky environ 15 fois plus rapide sur une série complète de données sur un serveur de développement. Bravo à abelenky! – amanda

0

chaînes C++ et cours d'eau sont très bien une bonne idée, mais si vous absolument ne pouvez pas les utiliser, le code suivant peut être utile. La première fonction écrit les deux doubles dans une chaîne existante. La deuxième fonction lit deux doubles sur une chaîne existante C et les renvoie par pointeur:

void CoordinatesToString(double lat, double long, char *buffer, size_t len) { 
    assert(buffer != NULL); 
    assert(len > 0); 

    snprintf(buffer, len, "%f, %f", lat, long); 
    buffer[len - 1] = 0; /* ensure null terminated */ 
} 

int StringToCoordinates(const char *string, double *outLatitude, double *outLongitude) { 
    assert(string != NULL); 
    assert(outLatitude != NULL); 
    assert(outLongitude != NULL); 

    return (2 == sscanf(string, "%f, %f", outLatitude, outLongitude)); 
} 

Utilisation:

char buffer[128]; 
CoordinatesToString(90.0, -33.0, buffer, 128); 

double lat, lng; 
if (StringToCoordinates(buffer, &lat, &lng)) { 
    printf("lat: %f long %f\n", lat, lng); 
} 

MAIS. Les chaînes C++ sont conçues pour ce type d'utilisation. Ils ne souffrent pas de problèmes de débordement potentiels inhérents aux tableaux char, et vous n'avez pas besoin de gérer manuellement leur mémoire - ils sont redimensionnés pour s'adapter à leur contenu si nécessaire. Y at-il une raison pourquoi vous voulez éviter std::string quand vous dites que vous êtes autrement d'accord avec une solution C++?

+0

Je ne pense pas avoir clarifié ma question. La longitude et la latitude peuvent être stockées dans un format de chaîne de degrés/minutes/secondes. J'ai besoin de convertir de cela en degrés décimaux stockés dans un double. Vraiment désolé. – amanda

+0

comme l'affiche a déclaré que sa principale préoccupation est la vitesse, snprintf et sscanf sont des choix HORRIBLE. Alors qu'ils sont incroyablement flexibles, ils sont incroyablement lents, car ils gèrent toutes sortes d'entrées et de sorties. – abelenky

+0

L'OP n'a * pas * mentionné la vitesse lorsque j'ai posté cette réponse. Comme indiqué initialement, @amanda a demandé seulement une solution qui n'utilisait pas 'std :: string'. La vitesse brute ne faisait pas partie de la question. –

2

Voici le code que je développé:

double Str2LatLong(char* coord) 
{ 
    // char* testInput = "47,26'14\""; 

    int i = 0; 
    int parts[3] = {0}; // parts[0] is degrees, parts[1] is minutes, parts[2] is seconds 
    int* pCurr = parts; 

    do 
    { 
     if (coord[i] == '\0') 
     { 
      break; 
     } 
     if (!isdigit(coord[i])) 
     { 
      *pCurr++; // skip from parts[0] ==> [1], or [1] ==> [2] 
      continue; 
     } 
     *pCurr = 10* (*pCurr) + coord[i] - '0'; 
     ++i; 
    } while (1); 

    return parts[0] + ((double)parts[1])/60.0 + ((double)parts[2])/3600.0; 
} 

Parce qu'il est écrit pour la vitesse, il n'y a pas de validation d'entrée.
Vous devez fournir une entrée correcte, sinon vous risquez de vous tromper. J'ai gardé tout en maths entiers, et la mémoire séquentielle du mieux que je pouvais.
Il ne vérifie pas les délimiteurs "corrects". Au contraire, chaque fois que quelque chose n'est pas un chiffre, il suppose que c'est la transition de degrés en minutes, ou de minutes en secondes.

Ce n'est qu'à la toute dernière ligne qu'il convertit en double avec quelques opérations en virgule flottante très simples.

Je suppose que vous voudrez le modifier pour gérer les valeurs positives/négatives et les indicateurs Nord/Sud, Est/Ouest et les décimales après les secondes. Mais je pense que ce code est une bonne base pour une routine de conversion très rapide. J'espère que cela va tester très rapidement. S'il vous plaît laissez-moi savoir comment ça se passe.

0

Voici une autre variante.

La logique est la même, mais elle utilise une instruction de casse pour une meilleure organisation, moins d'instructions break/continue et un retour plus rapide.

Vous devez également être en mesure d'améliorer celui-ci avec

case 'N': 
case 'S': 
case 'E': 
case 'W': 

au besoin.

double Str2LatLong(char* coord) 
{ 
    // char* testInput = "47,26'14\""; 

    int i = 0; 
    int parts[3] = {0}; 
    int* pCurr = parts; 

    while(1) 
    { 
     switch (coord[i]) 
     { 
      /* Any digit contributes to either degrees,minutes, or seconds (as *pCurr) */ 
      case '0': 
      case '1': 
      case '2': 
      case '3': 
      case '4': 
      case '5': 
      case '6': 
      case '7': 
      case '8': 
      case '9': 
       *pCurr = 10* (*pCurr) + coord[i] - '0'; 
       break; 

      /* A Null terminator triggers return (no break necessary) */ 
      case '\0': 
       return parts[0] + ((double)parts[1])/60.0 + ((double)parts[2])/3600.0; 

      /* Any other symbol moves pCurr from degrees to minutes, or minutes to seconds */ 
      default: 
       *pCurr++; 
     } 
     i++; 
    } 

    return -1.0; /* Should never reach this point! */ 
} 
+0

Celui-ci est très prometteur! Sur un serveur de développement, il semble être 17,6 fois plus rapide que tout ce que nous avons essayé. Cependant, il ne gère pas les chaînes décimales telles que: 50-50-50.2433N, donc je ne sais pas si c'est plus rapide ou juste parce que ça ne marche pas dans tous les cas. – amanda

+0

Je pense que le fait de gérer les décimales serait plutôt trivial. Le fait de gérer tous les cas peut être un peu difficile, mais pas insurmontable. – abelenky

+0

Sur 300mil, ce sont les seuls qui sont sortis incorrects. Puisque le code utilise des nombres entiers, je ne sais pas comment vous pourriez greffer les nombres décimaux sans passer à des nombres flottants ou est-ce que je manque quelque chose? Ne cherchez pas le code, juste la direction! – amanda

0

La partie difficile va représenter toutes les variations de format avec une seule grammaire. Une fois cela fait, vous pouvez utiliser un outil générateur de lexers pour cracher un DFA hautement optimisé qui sera compétitif avec le meilleur code réglé manuellement.