2010-11-17 21 views
7

Tôi nhận thấy rằng khi tôi lưu trữ một giá trị kép, ví dụ: x = 0.56657011973046234 trong cơ sở dữ liệu sqlite và sau đó truy xuất nó sau, tôi nhận được y = 0.56657011973046201. Theo số sqlite spec và số .NET spec (cả hai điều này tôi không đọc được đầu tiên :) điều này được mong đợi và bình thường.Làm thế nào tôi có thể 'cắt' một C# gấp đôi giá trị nó sẽ được lưu trữ như trong cơ sở dữ liệu sqlite?

Vấn đề của tôi là mặc dù độ chính xác cao không quan trọng, nhưng ứng dụng của tôi giao dịch với người dùng nhập/chọn đôi đại diện cho thông tin 3D cơ bản và sau đó chạy mô phỏng để tìm kết quả. Và đầu vào này có thể được lưu vào cơ sở dữ liệu sqlite để được tải lại và chạy lại sau.

Sự nhầm lẫn xảy ra do một loạt đầu vào mới được tạo sẽ rõ ràng mô phỏng theo cách hơi khác so với các đầu vào tương tự sau khi được lưu trữ và tải lại (vì các giá trị kép đã thay đổi). Điều này là hợp lý, nhưng không mong muốn.

Tôi chưa hoàn toàn biết cách xử lý vấn đề này, nhưng trong thời gian chờ đợi, tôi muốn giới hạn/kẹp đầu vào của người dùng vào các giá trị có thể được lưu trữ chính xác trong cơ sở dữ liệu sqlite. Vì vậy, nếu người dùng nhập 0.56657011973046234, nó thực sự được chuyển đổi thành 0.56657011973046201.

Tuy nhiên tôi đã không thể tìm ra, đưa ra một số, giá trị nào sẽ được lưu trữ trong cơ sở dữ liệu, ngắn thực sự lưu trữ và truy xuất nó từ cơ sở dữ liệu. Có cách nào để làm điều này không?

+3

Bạn biết đấy, nếu máy tính siêu nhỏ Royal McBee đã làm điều này, Edward Lorenz sẽ không bao giờ phát hiện ra lý thuyết hỗn độn. :) –

+0

@fostandy: Những giá trị thực tế đó (0.56657011973046234 và 0.56657011973046201) hay bạn đã tạo ra chúng? 0,56657011973046201 là 17 chữ số, do đó, nó nghĩa vụ phải vòng chuyến đi đến cùng một giá trị gấp đôi như 0.56657011973046234 (tôi đã kiểm tra trong C và nó không.) –

+0

@Rick Regan - 0.56657011973046234 là một cái gì đó tôi thu được từ random.NextDouble(). 0,56657011973046201 là những gì tôi thu được khi tôi lưu trữ nó trong một trường số sqlite và lấy nó một lần nữa. Theo tôi hiểu nó (có giới hạn) 2 số cuối không được bảo đảm cho chuyến đi khứ hồi. – fostandy

Trả lời

2

Vòng kép có triển khai với tham số chỉ định số chữ số. Sử dụng điều này để làm tròn đến 14 chữ số (nói) với: rval = Math.Round (Val, 14)

Sau đó làm tròn khi nhận giá trị từ cơ sở dữ liệu, và vào lúc bắt đầu mô phỏng, tức là. Vì vậy, tại các giá trị phù hợp?

Để biết chi tiết:

http://msdn.microsoft.com/en-us/library/75ks3aby.aspx

Một suy nghĩ nếu bạn không so sánh giá trị trong cơ sở dữ liệu, chỉ cần lưu trữ chúng: Tại sao không chỉ đơn giản là lưu trữ chúng dưới dạng dữ liệu nhị phân? Sau đó tất cả các bit sẽ được lưu trữ và phục hồi nguyên văn?

2

Giả sử cả SQL Lite và .NET thực hiện chính xác đặc tả IEEE, bạn sẽ có thể nhận được kết quả giống nhau nếu bạn sử dụng cùng loại điểm nổi trên cả hai mặt (vì giá trị không được thay đổi khi được chuyển từ cơ sở dữ liệu sang C# và ngược lại).

Hiện tại bạn đang sử dụng điểm nổi IEEE 8 byte (đơn) (*) trong SQL Lite và dấu phẩy động 16 byte trong C# (gấp đôi). Loại float trong C# tương ứng với chuẩn IEEE 8 byte, do đó, sử dụng loại này thay vì double có thể giải quyết được sự cố.

(*) Tài liệu SQL Lite nói rằng REAL là giá trị dấu phẩy động, được lưu trữ dưới dạng số dấu phẩy động IEEE 8 byte.

+0

Một phao C# là 4 byte, một C# double là 8 byte, vì vậy tôi đang sử dụng một điểm nổi 8-byte trong C# (double). Như vậy tôi ban đầu dự kiến ​​các giá trị sẽ giống hệt nhau sau khi truy xuất, nhưng chúng không phải do thực tế là C# double, trong khi _complying_ với IEEE spec, thực sự chứa thông tin bổ sung. SQLite không lưu thông tin này vì nó chỉ tuân thủ thông số IEEE. – fostandy

1

Bạn có thể sử dụng chuỗi để lưu # trong db. Cá nhân tôi đã thực hiện những gì winwaed đề nghị làm tròn trước khi lưu trữ và sau khi lấy từ db (được sử dụng số()).

Tôi nhớ lại bị đốt cháy bởi các chủ ngân hàng làm tròn nhưng nó có thể chỉ là không đáp ứng thông số kỹ thuật.

1

Bạn có thể lưu trữ các đôi như là một chuỗi, và bằng cách sử dụng định dạng khứ hồi khi chuyển đổi gấp đôi lên một chuỗi, nó được đảm bảo để tạo ra các giá trị tương tự khi phân tích cú pháp:

string formatted = theDouble.ToString("R", CultureInfo.Invariant); 
1

Nếu bạn muốn giá trị nhập thập phân cho chuyến đi khứ hồi, sau đó bạn sẽ phải giới hạn chúng thành 15 chữ số có nghĩa. Nếu bạn muốn các giá trị dấu phẩy động bên trong có độ chính xác gấp đôi trong SQLite, bạn có thể không may mắn; yêu cầu in tối thiểu 17 chữ số có nghĩa, nhưng từ những gì tôi có thể biết, SQLite in chúng tối đa là 15 (EDIT: có thể một chuyên gia SQLite có thể xác nhận điều này? Tôi chỉ đọc mã nguồn và truy tìm nó - Tôi đã chính xác, độ chính xác được giới hạn ở 15 chữ số.)

Tôi đã kiểm tra ví dụ của bạn trong giao diện lệnh SQLite trên Windows. Tôi đã chèn 0,56657011973046234 và chọn trả lại 0.566570119730462. Trong C, khi tôi giao 0,566570119730462 thành một đôi và in nó thành 17 chữ số, tôi nhận được 0,56657011973046201; đó là cùng một giá trị bạn nhận được từ C#. Bản đồ 0.56657011973046234 và 0,56657011973046201 cho các số dấu phẩy động khác nhau, vì vậy nói cách khác, gấp đôi SQLite không làm tròn.

3

Câu trả lời có thể là lưu trữ các giá trị kép là 17 chuỗi chữ số có nghĩa. Nhìn vào sự khác biệt giữa cách SQLite xử lý số thực so với văn bản (Tôi sẽ minh họa với giao diện dòng lệnh, vì đơn giản):

sqlite> create table t1(dr real, dt varchar(25)); 
sqlite> insert into t1 values(0.56657011973046234,'0.56657011973046234'); 
sqlite> select * from t1; 
0.566570119730462|0.56657011973046234 

lưu trữ nó có ái lực thực sự là nguyên nhân của vấn đề của bạn - chỉ SQLite cung cấp cho bạn trở lại xấp xỉ 15 chữ số. Nếu thay vào đó bạn lưu trữ nó dưới dạng văn bản, bạn có thể truy xuất chuỗi gốc bằng chương trình C# của mình và chuyển đổi nó trở lại thành giá trị gốc.

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