2015-06-29 14 views
5

Tôi có hai lĩnh vực để lưu trữ dữ liệu định vị, định nghĩa là tăng gấp đôi trong cơ sở dữ liệu MySQL của tôi:Strange giá trị thay đổi khi lưu dữ liệu double-type (geolocation) vào cơ sở dữ liệu

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

Và tôi đang sử dụng Yii2 của double validator trên giá trị được truyền bởi người sử dụng:

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

(mặc dù các bài kiểm tra của tôi dường như được chứng minh, rằng vấn đề này không có gì để làm với Validators Yii2)

trong thử nghiệm I'v e quan sát kỳ lạ thay đổi các giá trị, tức là (?):

  • 359.90 trở thành 359.899994 (0,000006 chênh lệch),
  • 359.80 trở thành 359.799988 (0,000012 chênh lệch),
  • 311.11 trở thành 311.109985 (0,000015 chênh lệch),
  • 255.55 trở thành 255.550003 (-0,000003 khác biệt),
  • 205.205 trở thành 205.205002 (-0,000002 khác biệt),
  • 105.105 trở thành 105.105003 (-0,000003 khác biệt).

nhưng:

  • 359.899994 vẫn 359.899994,
  • 311.109985 vẫn 311.109985,
  • 311 vẫn 311,
  • 255 vẫn 255,
  • 200 hài cốt 200,
  • 75.75 vẫn 75.75,
  • 11.11 vẫn 11.11.

Tôi đang thiếu gì? Tôi không thể nhìn thấy bất kỳ mô hình hoặc logic nào đằng sau chúng.

Đây có phải là do tôi có khai báo trường không đúng của MySQL cho loại dữ liệu này không? Nếu có, thì điều gì là đúng? Vài câu trả lời khác nhau:

gợi ý, rằng việc sử dụng float(10,6) là lựa chọn tốt nhất, nếu không sử dụng MySQL của spatial extensions.

xét nghiệm của tôi dường như được chứng minh, rằng vấn đề này không có gì để làm với Validators Yii2, bởi vì giá trị vẫn còn đúng cho đến khi được đọc từ cơ sở dữ liệu:

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! 
} 

Câu hỏi của tôi là trái với this one. Số của tôi tăng, không thua, chính xác! Và chỉ cho một số nhất định, không phải lúc nào.

+0

là các giá trị đúng trong * mysql * sau khi bạn lưu lại? – Tony

+0

@Tony Xin lỗi, tôi không hiểu câu hỏi của bạn, bởi vì tôi chưa bao giờ nói bất cứ điều gì về lần lưu thứ hai, chỉ về lần đọc thứ hai (đọc lại). Có, ngay sau khi giá trị '$ model-> save()' được lưu dưới dạng '$ model-> address_geo_latitude = 311.11' hóa ra thực sự lưu trữ/có giá trị' 311.109985' trong cột 'address_geo_latitude'. – trejder

Trả lời

2

Điều này xảy ra không phải vì Yii mà vì các giá trị dấu phẩy động được lưu trữ trên hệ nhị phân.

Như bạn có thể đọc trong MySQL documentation "Vấn đề với Floating-Point Values":

số Floating-point đôi khi gây nhầm lẫn, vì họ là gần đúng và không được lưu trữ như các giá trị chính xác. Giá trị dấu phẩy động là được viết trong một câu lệnh SQL có thể không giống như giá trị được biểu thị trong nội bộ.

Here you can find the great explanation cho vấn đề này với ví dụ. Như bạn có thể thấy các con số có thể lớn hơn một chút, nhỏ hơn hoặc không thay đổi chút nào nhưng bạn luôn phải nhớ rằng đây chỉ là một phép tính xấp xỉ.

Để biết dữ liệu vị trí bạn có thể sử dụng đơn giản DECIMAL type để đảm bảo giá trị được lưu trữ không thay đổi trong cơ sở dữ liệu hoặc sử dụng Spacial Data type được tối ưu hóa để lưu trữ và truy vấn dữ liệu đại diện cho đối tượng được xác định trong không gian hình học.

+0

Như tôi đã viết trong câu hỏi của tôi (tôi đã làm?) Làm tròn hoặc xấp xỉ được biết đến với tôi, nhưng nó không có gì để làm với vấn đề này. Làm tròn luôn hướng về số gần nhất, cả về lập trình và toán học. Đó là về mất chính xác, không đạt được nó. Bạn không thể gọi thay đổi '105.105' thành' 105.105003' một _rounding_ bởi vì đó không phải là làm tròn ở tất cả và đó là cả một hoạt động vô lý và toán học không chính xác. – trejder

+1

@trejder phiên bản trước đó của tài liệu mysql được đề cập trong câu trả lời https://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html có một ví dụ tốt về cách các số dấu phẩy động khác nhau có thể được biểu diễn khi bạn thêm nhiều chữ số thập phân, vì vậy hãy thử sử dụng * decimal * thay vì * float * và so sánh kết quả – Tony

+0

@Bizley, bạn có thể chỉnh sửa câu trả lời của mình, bao gồm hầu hết các nhận xét ở đây và có thể liên kết [câu trả lời này] (http : //stackoverflow.com/a/163084/1469208). Bạn đã thực sự giải quyết được vấn đề! :> Thay đổi 'float (10,6)' thành 'decimal (9,6)' tạo ra một phép lạ và không có thay đổi giá trị nào xuất hiện. Tôi sẽ rất vui khi chấp nhận câu trả lời của bạn, sau khi bạn chỉnh sửa nó. – trejder

Các vấn đề liên quan