[Talk-de] Genauigkeit der Koordinaten in der OSM-DB (Nachkommastellen)
Christian Müller
cmue81 at gmx.de
Mi Jan 15 09:05:35 UTC 2025
> Gesendet: Montag, 13. Januar 2025 um 11:45
> Von: "Frederik Ramm" <frederik at remote.org>
> An: "Christian Müller via Talk-de" <talk-de at openstreetmap.org>
> Betreff: Re: [Talk-de] Genauigkeit der Koordinaten in der OSM-DB (Nachkommastellen)
>
> In OSM werden die Daten intern nicht als float-Werte gespeichert,
> sondern als Ganzzahlen, und vorher mit 1E7 multipliziert:
>
> https://github.com/openstreetmap/openstreetmap-website/blob/5d76ec051e2c429b6647401674e13688c1251956/app/models/concerns/geo_record.rb#L17-L20
Bevor die Wandlung in eine Ganzzahl erfolgt,
führt die API mit den Upload-Daten das hier
durch:
https://github.com/openstreetmap/openstreetmap-website/blob/914fff9d4dec3d502d524f5b0f60797f66f8af65/app/models/node.rb#L91
(node.lat = OSM.parse_float...)
https://github.com/openstreetmap/openstreetmap-website/blob/914fff9d4dec3d502d524f5b0f60797f66f8af65/lib/osm.rb#L510
(parse_float def)
Float(..) in der Funktion parse_float
wandelt eingehende Längen- und Breitengradwerte,
die als Zeichenkette an die API übermittelt werd-
en in den Ruby-Typ Float (laut Ruby-Dok ist das
nativ float64)
Neben der in der vorigen mail bereits erwähnten
"Alternative mittels Zeichenkettenoperationen"
zur Wandlung dieser Werte in das von der DB
verwendete Ganzzahlformat, bestünde noch die
Alternative mittels Decimal (in Ruby BigDecimal):
--------------------------------------------------
>>> def to_int(l):
... r = Decimal(l) * Decimal('1e7')
... r = r.quantize(Decimal('1.'), rounding=ROUND_HALF_UP)
... return int(r)
...
>>> to_int(1.67772165)
16777217
>>> to_int(1.67772175)
16777217
>>> Decimal(1.67772175) * Decimal('1e7')
Decimal('16777217.49999999900637703831')
>>> to_int('1.67772175')
16777218
Quellen:
https://docs.python.org/3/library/decimal.html
https://ruby-doc.org/stdlib-3.1.0/libdoc/bigdecimal/rdoc/BigDecimal.html
Die letzten beiden Beispiele unterscheiden
sich darin, welcher Typ dem Konstruktur von
Decimal übergeben wird. Beim ersten wird das
Literal 1.67772175 zunächt in den nativen
Fließkommazahltyp gewandelt und dessen Wert
von Decimal übernommen. Dieser Zwischenschritt
enfällt, wenn Decimal direkt mit der Zeichenket-
te konstruiert wird (die bei der OSM-API während
des Uploads so vorliegt..).
Die Ruby-Dokumentation, siehe letzte Quellen-
angabe, zu BigDecimal schreibt:
"Decimal arithmetic is also useful for general
calculation, because it provides the correct
answers people expect–whereas normal binary
floating point arithmetic often introduces
subtle errors because of the conversion between
base 10 and base 2."
Für Java existieren Angaben, dass BigDecimal
um den Faktor 1000 langsamer sein kann, als
Double. Außerdem wäre der Speicherbedarf etwas
höher, während die API die Anfrage bearbeitet.
Im Kontext Ruby lassen sich ad hoc keine brauch-
baren Benchmarks finden, aber es ist anzunehmen,
dass die Situation ähnlich ist.
Man könnte im Ruby-Code beide (bzw. alle drei)
Varianten parallel implementieren und auf einem
Testserver messen, ob der Unterschied produktiv
relevant wäre.
Falls sich herausstellt, dass der Einsatz von
Float für die Performance nicht kritisch ist,
kann man die Wiedergabetreue von Werten, die
an die DB übermittelt wurden, erhöhen. Ob das
beim praktischen Einsatz bemerkt würde, ist
eine andere Frage.
Der Effekt müsste sich unabhängig davon ein-
stellen, wie groß SCALE gewählt wird, also
auch unter Beibehaltung des derzeitigen Werts
samt 'int4'-Typ für die Datenhaltung (mini-
male) Vorteile bringen.
Gruß
Mehr Informationen über die Mailingliste Talk-de