2012-03-08 25 views
11

Tôi đang viết một chương trình mô phỏng tiến hành theo các bước riêng biệt. Mô phỏng bao gồm nhiều nút, mỗi nút có một giá trị dấu phẩy động được liên kết với nó, được tính toán lại trên mỗi bước. Kết quả có thể là dương, âm hoặc bằng không.Làm thế nào tôi có thể nhận được hành vi chương trình nhất quán khi sử dụng phao nổi?

Trong trường hợp kết quả bằng không hoặc ít xảy ra điều gì đó. Cho đến nay điều này dường như đơn giản - Tôi chỉ có thể làm một cái gì đó như thế này cho mỗi nút:

if (value <= 0.0f) something_happens(); 

Một vấn đề đã phát sinh, tuy nhiên, sau khi một số thay đổi gần đây tôi thực hiện cho các chương trình trong đó tôi sắp xếp lại thứ tự mà tính toán nhất định được thực hiện. Trong một thế giới hoàn hảo, các giá trị sẽ vẫn xuất hiện tương tự sau khi sắp xếp lại, nhưng vì sự thiếu chính xác của biểu diễn dấu phẩy động, chúng xuất hiện rất khác nhau một chút. Vì các phép tính cho mỗi bước phụ thuộc vào kết quả của bước trước đó, những thay đổi nhỏ trong kết quả này có thể tích lũy thành các biến thể lớn hơn khi số tiền thu được mô phỏng.

Dưới đây là một chương trình ví dụ đơn giản thể hiện những hiện tượng tôi đang mô tả:

float f1 = 0.000001f, f2 = 0.000002f; 
f1 += 0.000004f; // This part happens first here 
f1 += (f2 * 0.000003f); 
printf("%.16f\n", f1); 

f1 = 0.000001f, f2 = 0.000002f; 
f1 += (f2 * 0.000003f); 
f1 += 0.000004f; // This time this happens second 
printf("%.16f\n", f1); 

Kết quả của chương trình này là

0.0000050000057854 
0.0000050000062402 

mặc dù Ngoài ra là hoán vì vậy cả hai kết quả nên giống nhau . Lưu ý: Tôi hiểu rõ lý do tại sao điều này đang xảy ra - đó không phải là vấn đề. Vấn đề là các biến thể này có thể có nghĩa là đôi khi một giá trị được sử dụng để đi ra âm trên bước N, kích hoạt something_happens(), bây giờ có thể xuất hiện một hoặc hai bước âm sớm hơn hoặc muộn hơn, có thể dẫn đến kết quả mô phỏng tổng thể rất khác nhau vì something_happens() có hiệu ứng lớn. Những gì tôi muốn biết là có một cách hay để quyết định khi nào something_happens() nên được kích hoạt mà sẽ không bị ảnh hưởng bởi các biến thể nhỏ trong kết quả tính toán do các hoạt động sắp xếp lại để hành vi các phiên bản mới hơn của chương trình của tôi sẽ nhất quán với các phiên bản cũ hơn.

Các giải pháp duy nhất mà tôi cho đến nay đã có thể nghĩ đến là sử dụng một số epsilon giá trị như thế này:

if (value < epsilon) something_happens(); 

nhưng vì các biến thể nhỏ trong các kết quả tích lũy theo thời gian tôi cần phải thực hiện epsilon khá lớn (tương đối nói) để đảm bảo rằng các biến thể không dẫn đến something_happens() được kích hoạt trên một bước khác. Có cách nào tốt hơn?

Tôi đã đọc this excellent article về so sánh điểm nổi, nhưng tôi không thấy cách bất kỳ phương pháp so sánh nào được mô tả có thể giúp tôi trong trường hợp này.

Lưu ý: Sử dụng giá trị số nguyên thay vì không phải là một tùy chọn.


Sửa khả năng sử dụng đôi thay vì nổi đã được nâng lên. Điều này sẽ không giải quyết được vấn đề của tôi vì các biến thể vẫn ở đó, chúng sẽ chỉ có độ lớn nhỏ hơn.

+4

Nếu các biến thể nhỏ gây ra thay đổi lớn về đầu ra, điều đó có nghĩa là kết quả của bạn có độ chính xác thấp không? (Ngoài ra: tại sao phao không tăng gấp đôi?) –

+3

Hãy cẩn thận: 'printf ("%. 16f \ n ", f1);' đây là một tác dụng phụ không mong muốn: nó sẽ chuyển đổi phao của bạn thành một số tăng gấp đôi không đáng kể. Một float, tôi nghĩ, độ chính xác 7 chữ số ở mức tối đa. –

+4

Cách tiêu chuẩn là tổng các giá trị dấu phẩy động là: để sắp xếp chúng và tổng hợp từ nhỏ nhất đến lớn nhất, độ chính xác nhỏ nhất này mất đi. Cũng sử dụng đôi không nổi. –

Trả lời

0

Tôi khuyên bạn nên thực hiện một bước - tốt nhất là ở chế độ lắp ráp - thông qua các phép tính trong khi thực hiện cùng một số học trên máy tính. Bạn sẽ có thể xác định thứ tự tính toán nào mang lại kết quả có chất lượng thấp hơn bạn mong đợi và chất lượng nào hoạt động. Bạn sẽ học hỏi từ điều này và có thể viết các tính toán được sắp xếp tốt hơn trong tương lai.

Cuối cùng - đưa ra ví dụ về các số bạn sử dụng - có thể bạn sẽ cần phải chấp nhận thực tế là bạn sẽ không thể thực hiện so sánh bình đẳng.

Đối với phương pháp tiếp cận epsilon, bạn thường cần một epsilon cho mọi số mũ có thể. Đối với định dạng điểm nổi chính xác đơn, bạn sẽ cần 256 giá trị dấu chấm động chính xác đơn lẻ khi số mũ là 8 bit. Một số số mũ sẽ là kết quả của ngoại lệ nhưng đối với sự đơn giản, tốt hơn là có một 256 thành viên vectơ hơn để thực hiện nhiều thử nghiệm. Một cách để thực hiện điều này có thể là xác định epsilon cơ sở của bạn trong trường hợp số mũ là 0 i e giá trị được so sánh so với trong khoảng 1.0 < = x < 2.0.Tốt hơn là epsilon nên được chọn làm giá trị iea cơ bản 2 có thể được biểu diễn chính xác trong một định dạng điểm nổi chính xác duy nhất - theo cách đó bạn biết chính xác những gì bạn đang thử nghiệm và sẽ không phải suy nghĩ về các vấn đề làm tròn trong epsilon tốt. Đối với số mũ -1 bạn sẽ sử dụng epsilon cơ sở của bạn chia cho hai, cho -2 chia cho 4 và cứ thế. Khi bạn tiếp cận các phần thấp nhất và cao nhất của dãy số mũ, bạn dần dần cạn kiệt chính xác từng chút một - vì vậy bạn cần phải biết rằng các giá trị cực đoan có thể khiến phương pháp epsilon thất bại.

4

Tôi đã làm việc với các mô hình mô phỏng trong 2 năm và cách tiếp cận epsilon là cách tốt nhất để so sánh các phao của bạn.

0

Nếu nó hoàn toàn phải là phao thì sử dụng giá trị epsilon có thể giúp nhưng không thể loại bỏ mọi vấn đề. Tôi khuyên bạn nên sử dụng tăng gấp đôi cho các điểm trong mã bạn biết chắc chắn sẽ có biến thể. Một cách khác là sử dụng phao để thi đua đôi, có rất nhiều kỹ thuật và cách cơ bản nhất là sử dụng 2 phao và thực hiện một chút toán để tiết kiệm phần lớn số trong một phao và phần còn lại trong khác (nhìn thấy một hướng dẫn tuyệt vời về điều này, nếu tôi tìm thấy nó, tôi sẽ liên kết nó).

3

Nói chung, việc sử dụng các giá trị epsilon phù hợp là cách để đi nếu bạn cần sử dụng các số dấu phẩy động. Dưới đây là một số điều có thể giúp ích:

  • Nếu giá trị của bạn nằm trong phạm vi đã biết và bạn không cần chia rẽ, bạn có thể chia tỷ lệ vấn đề và sử dụng các phép toán chính xác. Nói chung, các điều kiện không áp dụng.
  • Biến thể là sử dụng các số hữu tỷ để thực hiện tính toán chính xác. Điều này vẫn có những hạn chế đối với các hoạt động có sẵn và nó thường có các tác động hiệu suất nghiêm trọng: bạn thực hiện giao dịch về tính chính xác.
  • Chế độ làm tròn có thể được thay đổi. Điều này có thể được sử dụng để tính toán một khoảng thời gian chứ không phải là một giá trị riêng lẻ (có thể với 3 giá trị kết quả từ làm tròn lên, làm tròn xuống và tròn gần nhất). Một lần nữa, nó sẽ không làm việc cho tất cả mọi thứ nhưng bạn có thể nhận được một ước tính lỗi trong số này.
  • Việc theo dõi giá trị và một số thao tác (có thể có nhiều bộ đếm) cũng có thể được sử dụng để ước tính kích thước lỗi hiện tại.
  • Để có thể thử nghiệm với các biểu diễn số khác nhau (float, double, khoảng thời gian, v.v.), bạn có thể muốn triển khai mô phỏng của mình dưới dạng mẫu được tham số cho loại số.
  • Có nhiều sách được viết trên ước tính và giảm thiểu lỗi khi sử dụng số học dấu phẩy động. Đây là chủ đề của toán học số.

Hầu hết các trường hợp tôi biết ngắn gọn về thử nghiệm với một số phương pháp được đề cập ở trên và kết luận rằng mô hình là không chính xác và không bận tâm đến nỗ lực này. Ngoài ra, làm điều gì đó khác hơn là sử dụng float có thể mang lại kết quả tốt hơn nhưng quá chậm, thậm chí sử dụng double do bộ nhớ gấp đôi và cơ hội nhỏ hơn khi sử dụng các hoạt động SIMD.

+0

Trong một số trường hợp "bình thường hóa" (?) Có thể hữu ích. Giả sử một đối tượng đang di chuyển hoặc xoay và các tọa độ mới của nó được tính toán lại mỗi lần dựa trên các tọa độ cuối cùng. Với thời gian, khi các lỗi tích tụ, đối tượng có thể bắt đầu mất hình dạng hoặc kích thước ban đầu của nó. Điều này có thể được cố định bằng cách tính toán lại/điều chỉnh tọa độ một cách thích hợp (mỗi lần hoặc một lần trong một thời gian), có tính đến hình dạng và kích thước mong đợi. –

0

Chắc chắn bạn nên sử dụng tăng gấp đôi thay vì nổi. Điều này có thể sẽ làm giảm số lượng các nút lộn ngược đáng kể.

Nói chung, việc sử dụng ngưỡng epsilon chỉ hữu ích khi bạn so sánh hai số dấu phẩy động cho bình đẳng, không phải khi bạn so sánh chúng để xem số nào lớn hơn. Vì vậy, (đối với hầu hết các mô hình, ít nhất) bằng cách sử dụng epsilon sẽ không đạt được bạn bất cứ điều gì cả - nó sẽ chỉ thay đổi tập hợp các nút lật, nó sẽ không làm cho rằng thiết lập nhỏ hơn. Nếu chính mô hình của bạn hỗn loạn, thì nó hỗn loạn.

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