2015-06-29 12 views
5

mam dwa pola do przechowywania danych geolokalizacyjnych, zdefiniowanych jako podwaja w mojej bazy danych MySQL:Dziwna zmiana wartości podczas zapisywania danych podwójnego typu (geolokalizacji) do bazy

`address_geo_latitude` float(10,6) NOT NULL, 
`address_geo_longitude` float(10,6) NOT NULL 

i używam Yii2 użytkownika double walidator na wartości przekazywane przez użytkownika:

public function rules() 
{ 
    return [ 
     [['address_geo_latitude', 'address_geo_longitude'], 'double', 'min'=>0, 'max'=>360] 
    ]; 
} 

(chociaż moje badania wydaje się być udowodnienie, że kwestia ta nie ma nic wspólnego z walidatorami Yii2)

podczas testów I'v e zaobserwować dziwne zmiany wartości, to jest (?):

  • 359.90 staje 359.899994 (0,000006 różnica),
  • 359.80 staje 359.799988 (0,000012 różnica),
  • 311.11 staje 311.109985 (0,000015 różnica),
  • 255.55 staje się 255.550003 (-0,000003 różnica),
  • się 205.205002 (-0,000002 różnicy),
  • 105.105 się 105.105003 (-0,000003 różnica).

ale:

  • 359.899994 pozostaje 359.899994,
  • 311.109985 pozostaje 311.109985,
  • 311 pozostaje 311,
  • 255 pozostaje 255,
  • 200 pozostałości 200,
  • 75.75 pozostaje 75.75,
  • 11.11 pozostaje 11.11.

Czego mi brakuje? Nie widzę żadnego wzoru ani logiki za nimi.

Czy to dlatego, że mam niepoprawną deklarację pola MySQL dla tego rodzaju danych? Jeśli tak, to jaki jest właściwy? Kilka różnych odpowiedzi:

wskazuje, że za pomocą float(10,6) jest najlepszym rozwiązaniem, jeśli nie przy użyciu MySQL spatial extensions.

moich testów wydaje się być udowodnienie, że kwestia ta nie ma nic wspólnego z walidatorami Yii2, ponieważ wartość pozostaje poprawna aż ponownie odczytane z bazy danych:

print_r(Yii::$app->request->post()); //Correct! 
print_r($lab->address_geo_latitude); //Correct! 

if ($lab->load(Yii::$app->request->post(), 'Lab') && $lab->save()) { 
    print_r($lab->address_geo_latitude); //Correct! 

    $lab2 = $this->findModel($lab->id); 
    print_r($lab2->address_geo_latitude); //<-- HERE! Incorrect! 
} 

Moje pytanie jest sprzeczny this one. Moje numery: zyskują, nie tracę, dokładność! I tylko dla pewnych liczb, nie zawsze.

+0

Czy wartości są poprawne w * mysql * po ponownym zapisaniu? – Tony

+0

@ Tony Niestety, nie rozumiem twojego pytania, ponieważ nigdy nie powiedziałem nic o drugim zapisie, tylko o drugim czytaniu (ponownym przeczytaniu). Tak, natychmiast po zapisaniu '$ model-> save()' jako '$ model-> address_geo_latitude = 311.11' okazuje się faktycznie przechowywać/ma wartość' 311.109985' w kolumnie 'address_geo_latitude'. – trejder

Odpowiedz

2

Dzieje się tak nie z powodu Yii, ale z powodu tego, jak wartości zmiennoprzecinkowe są przechowywane w systemach dwójkowych.

Jak można przeczytać w „Problemy z wartości zmiennoprzecinkowych” MySQL documentation:

Liczby zmiennoprzecinkowe czasami powodować nieporozumienia, ponieważ są one przybliżone i nie są przechowywane jako dokładnych wartości. Wartość zmiennoprzecinkowa jako zapisana w instrukcji SQL może nie być taka sama jak wartość reprezentowana wewnętrznie.

Here you can find the great explanation za ten problem z przykładami. Jak widać liczby mogą być nieco większe, mniejsze lub wcale nie zmienione, ale zawsze trzeba pamiętać, że jest to tylko przybliżenie.

Dla danych gelocation można użyć prostego DECIMAL type, aby upewnić się, że wartości są przechowywane niezmienione w bazie danych lub użyć Spacial Data type zoptymalizowanej do przechowywania i wysyłania zapytań dotyczących danych reprezentujących obiekty zdefiniowane w przestrzeni geometrycznej.

+0

Jak napisałem w moim pytaniu (czy ja?) Zaokrąglenie lub przybliżenie jest mi znane, ale nie ma to nic wspólnego z tym problemem. Zaokrąglanie jest zawsze w kierunku najbliższej liczby, zarówno w programowaniu, jak i matematyce. Chodzi o utratę precyzji, a nie uzyskanie jej. Nie możesz wywołać zmiany '105.105' na' 105.105003' a _rounding_ ponieważ to nie jest zaokrąglanie w ogóle i jest to zarówno operacja absurdalna, jak i niepoprawna matematycznie. – trejder

+1

@trejder wcześniejsza wersja dokumentacji mysql wymieniona w odpowiedzi https://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html ma dobry przykład tego, jak różne liczby zmiennoprzecinkowe mogą być reprezentowane, gdy dodajesz więcej miejsc dziesiętnych, więc spróbuj użyć * dziesiętnej * zamiast * zmiennoprzecinkowego * i porównaj wyniki – Tony

+0

@Bizley, czy możesz zmienić swoją odpowiedź, w tym większość tych komentarzy tutaj i być może łącząc [tę odpowiedź] (http : //stackoverflow.com/a/163084/1469208). Naprawdę rozwiązałeś problem! :> Zmiana "float (10,6)" na "dziesiętny (9,6)" powoduje cud i nie pojawia się żadna zmiana wartości. Z radością przyjmuję twoją odpowiedź, gdy ją ponownie przetworzę. – trejder