Tại sao độ phân giải của số dấu phẩy động giảm hơn nữa từ gốc?


19

Cảnh OpenGL của tôi có các đối tượng được định vị ở khoảng cách rất xa so với điểm gốc. Khi tôi xem các đối tượng này và xoay / xoay / thu phóng camera xung quanh chúng, chúng sẽ bị rung. Đó là, các đỉnh bao gồm các đối tượng dường như chụp xung quanh một lưới các điểm 3d tưởng tượng. Tôi đã đọc đây là một vấn đề phổ biến vì lượng thông tin có thể được lưu trữ bằng độ chính xác của dấu phẩy động (mà OpenGL và khá nhiều thứ khác sử dụng). Tôi không hiểu tại sao điều này xảy ra mặc dù.

Khi tìm kiếm một giải pháp, tôi đã tìm thấy cách khắc phục 'nguồn gốc nổi' rất đơn giản và dường như nó hoạt động. Tôi chỉ biến đổi mọi thứ để các đối tượng của tôi ở cùng một vị trí tương đối nhưng bất cứ thứ gì máy ảnh của tôi nhìn đều gần với điểm gốc. Tôi đã tìm thấy một lời giải thích ở đây: http://floatingorigin.com/ , nhưng tôi không thể làm theo nó.

Vậy ... ai đó có thể giải thích tại sao định vị cảnh của tôi ở rất xa (giả sử 10 triệu đơn vị) từ nguồn gốc dẫn đến hành vi thất thường mà tôi quan sát được không? Và cũng tại sao di chuyển nó gần với nguồn gốc khắc phục vấn đề?


4
Bởi vì nếu không, chúng sẽ là các số điểm cố định . Câu hỏi Tautological, này.
MSalters

1
Đúng, nhưng chỉ khi bạn hiểu "điểm nổi" thực sự có nghĩa là gì.
Kylotan

Câu trả lời:


26

Đây là TẤT CẢ do cách các điểm nổi được thể hiện trong máy tính.

Số nguyên được lưu trữ khá đơn giản; mỗi đơn vị chính xác là "một" ngoài "trước" như bạn mong đợi với số lượng có thể đếm được.

Với số dấu phẩy động, đây không phải là trường hợp chính xác. Thay vào đó, một số bit chỉ ra EXPONENT và phần còn lại chỉ ra phần được gọi là phần mantissa hoặc phần phân đoạn sau đó được NHIỀU bởi phần số mũ (ngầm là 2 ^ exp) để đưa ra kết quả cuối cùng.

Nhìn vào đây để giải thích trực quan của các bit.

Chính vì số mũ này là một phần thực tế của các bit mà độ chính xác bắt đầu để WANE khi số lượng ngày càng lớn.

Để thấy điều này trong thực tế, chúng ta hãy thực hiện một biểu diễn dấu phẩy động giả mà không đi vào nitty-gritty: lấy số mũ nhỏ như 2 và thực hiện một số phần phân số để kiểm tra:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

... Vv

Những con số này không phát triển rất xa nhau chỉ theo số mũ 2. Nhưng bây giờ, hãy thử số mũ 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Whoa, sự khác biệt rất lớn bây giờ!

Ví dụ, trong khi không đặc biệt đi đến RẤT NHIỀU TIẾP THEO (đó sẽ là phần phân đoạn tiếp theo tùy thuộc vào số lượng bit), có để chứng minh tổn thất chính xác khi số tăng lên. Đơn vị "đếm tiếp theo" trong phao rất nhỏ với số mũ nhỏ và RẤT lớn với số mũ lớn hơn, trong khi ở số nguyên thì LUÔN LUÔN 1.

Lý do phương pháp gốc phao hoạt động là bởi vì nó nhân rộng tất cả các số dấu phẩy động có số mũ lớn có khả năng này XUỐNG theo số mũ nhỏ để "số đếm tiếp theo" (độ chính xác) có thể rất nhỏ và hạnh phúc.


Các ví dụ bạn đã đưa ra thực sự minh họa, cảm ơn :)
Pris

3
Đi đúng hướng, nhưng tôi ước bạn đã sử dụng các ví dụ gần với cách điểm nổi thực sự hoạt động. Nó không nâng mantissa lên số mũ; đó là mantissa * 2 ^ số mũ.
Nathan Reed

3
Bạn nói đúng, tôi biết điều đó; Tôi không biết mình đang nghĩ gì. Chỉnh sửa câu trả lời của tôi.

1
@ScottW Chỉnh sửa đẹp! +1
Nathan Reed

17

Bởi vì số dấu phẩy động được biểu diễn dưới dạng phân số + số mũ + và bạn chỉ có một lượng bit cố định cho phần phân số.

http://en.wikipedia.org/wiki/Single_precision

Khi bạn nhận được số lớn hơn và lớn hơn, đơn giản là bạn không có các bit để biểu thị các phần nhỏ hơn.


8

Cổ điển trong lĩnh vực này phải được đưa lên: Điều mà mọi nhà khoa học máy tính nên biết về số dấu phẩy động .

Nhưng ý chính của nó liên quan đến việc làm thế nào các số dấu phẩy động chính xác (đôi) chỉ là một số nhị phân 32 bit (64 bit) với 1 bit đại diện cho dấu hiệu, số mũ 8 bit (11 bit) của cơ sở 2 và có ý nghĩa 23 bit (52 bit) (dấu ngoặc đơn là giá trị nhân đôi).

Điều đó có nghĩa là số dương nhỏ nhất bạn có thể biểu thị với độ chính xác đơn là 0,000000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1.40 x 10 -45 .

Số dương tiếp theo gấp đôi: 0,00000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , và sau đó số tiếp theo là tổng của hai 0,000000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .

Điều này tiếp tục tăng bởi cùng một sự khác biệt không đổi cho đến khi: 0.111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1.17549435 x 10 -38 - 0.00000014 x 10 -38 = 1.17549421 x 10 -38

Bây giờ bạn đã đạt được các số bình thường (trong đó chữ số đầu tiên trong số có nghĩa là 1) cụ thể: 1.0000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38 và số tiếp theo là 1.0000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023.


2

Lý do tại sao các số dấu phẩy động trở nên kém chính xác hơn từ gốc là bởi vì một số dấu phẩy động được cho là có thể biểu thị các số lớn. Cách thức này được thực hiện cho vay thuật ngữ "điểm nổi". Nó phân tách các giá trị có thể có (được xác định bởi độ dài bit của nó) để có cùng một số cho mỗi số mũ: Đối với số float 32 bit, 23 bit xác định mantissa hoặc có nghĩa. Vì vậy, nó sẽ có thể lấy giá trị của 2 ^ 23 giá trị khác nhau trong mỗi phạm vi số mũ. Một trong những phạm vi số mũ này là 1-2 [2 ^ 0 đến 2 ^ 1], do đó việc chia phạm vi từ 1 đến 2 thành 2 ^ 23 giá trị khác nhau cho phép có nhiều độ chính xác.

Nhưng việc chia phạm vi [2 ^ 10 đến 2 ^ 11] thành 2 ^ 23 giá trị khác nhau có nghĩa là khoảng cách giữa mỗi giá trị lớn hơn rất nhiều. Nếu không, thì 23 bit sẽ không đủ. Toàn bộ điều này là một sự thỏa hiệp: Bạn cần một số lượng bit vô hạn để thể hiện bất kỳ số thực nào. Nếu ứng dụng của bạn hoạt động theo cách cho phép bạn thoát khỏi độ chính xác thấp hơn cho các giá trị lớn hơn và bạn được hưởng lợi từ việc có thể thực sự đại diện cho các giá trị lớn , bạn sử dụng biểu diễn dấu phẩy động.


chỉ cần ghi chú ở đây sau khi xem xét thêm 7 năm sau ... số của tôi trong các ví dụ của tôi không được lựa chọn đặc biệt. Nhưng các điểm tổng thể là hợp lệ.
Steven Lu

1

Có thể hơi khó để đưa ra các ví dụ cụ thể về cách hoạt động của độ chính xác của dấu phẩy động. Để bổ sung cho các câu trả lời khác, đây là một. Giả sử chúng ta có một số dấu phẩy động thập phân , với ba chữ số mantissa và một chữ số mũ:

mantissa × 10 số mũ

Khi số mũ bằng 0, mọi số nguyên trong phạm vi 0 Biến999 có thể được biểu diễn chính xác. Khi là 1, về cơ bản, bạn nhân mọi yếu tố của phạm vi đó với 10, vì vậy bạn nhận được phạm vi 0 Hồi9990; nhưng bây giờ, chỉ có bội số của 10 có thể được biểu diễn chính xác, bởi vì bạn vẫn chỉ có ba chữ số chính xác. Khi số mũ ở mức tối đa là 9, chênh lệch giữa mỗi cặp số nguyên có thể biểu thị là một tỷ . Bạn thực sự giao dịch chính xác cho phạm vi.

Nó hoạt động theo cùng một cách với các số dấu phẩy động nhị phân: bất cứ khi nào số mũ tăng lên một, phạm vi sẽ tăng gấp đôi , nhưng số lượng giá trị đại diện trong phạm vi đó bị giảm đi một nửa . Điều này cũng áp dụng cho các số phân số, tất nhiên đó là nguồn gốc của vấn đề của bạn.


0

Nói chung, độ phân giải trở nên tồi tệ hơn vì độ phân giải được nhân với giá trị số mũ (phần số mũ 2 **).

thừa nhận nhận xét của josh: ở trên chỉ là đưa câu trả lời vào một tuyên bố cô đọng. Tất nhiên, như tôi đã cố gắng chỉ ra trên http://floatingorigin.com/ , đây mới chỉ là bắt đầu hướng tới một giải pháp tổng thể và chương trình của bạn có thể có jitter từ một số nơi: trong đường ống chính xác hoặc các phần khác của mã .


Điều này không thêm bất cứ điều gì chưa có trong các câu trả lời khác.

Đúng: Tôi nhận ra rằng tôi có thể mô tả câu trả lời trong một dòng duy nhất và nghĩ rằng ai đó có thể tìm thấy một câu trả lời ngắn gọn hữu ích.
Chris Thorne

-1

Bộ đệm độ sâu OpenGL không tuyến tính . Bạn càng đi xa, độ phân giải càng tệ. Tôi khuyên bạn nên đọc . Một cái gì đó được lấy từ đó (12.070):

Tóm lại, sự phân chia phối cảnh, theo bản chất của nó, gây ra độ chính xác Z gần với mặt trước của khối lượng xem hơn gần mặt sau.

Và một số khác (12.040):

Bạn có thể đã cấu hình các mặt phẳng cắt zNear và zFar theo cách hạn chế nghiêm trọng độ chính xác của bộ đệm sâu. Nói chung, điều này được gây ra bởi giá trị mặt phẳng cắt zNear quá gần với 0,0. Khi mặt phẳng cắt zNear được đặt ngày càng gần với 0,0, độ chính xác hiệu quả của bộ đệm sâu giảm đáng kể. Di chuyển mặt phẳng cắt zFar ra xa mắt luôn có tác động tiêu cực đến độ chính xác của bộ đệm độ sâu, nhưng nó không ấn tượng bằng việc di chuyển mặt phẳng cắt zNear.

Vì vậy, bạn nên di chuyển máy bay gần của bạn xa nhất có thể và máy bay xa của bạn gần nhất có thể.


-1: câu hỏi là về độ chính xác của dấu phẩy động, không phải là các vấn đề chính xác với biểu diễn bộ đệm độ sâu phi tuyến tính.
Nathan Reed

Có thể những gì tôi đang thấy là do vấn đề đệm sâu. Tôi đang sử dụng lib trên đỉnh OpenGL để xem cảnh của mình và tôi đang giả định rằng nó sẽ thiết lập máy ảnh, chế độ xem và cắt các mặt phẳng gần và xa để tính kích thước và vị trí của hình học (vì trình xem công cụ dường như tự động thiết lập chế độ xem tối ưu cho nội dung cảnh). Nhưng tôi đoán đây có thể không phải là trường hợp - tôi sẽ thử chơi xung quanh với các mặt phẳng cắt để giữ nguyên vị trí ban đầu và xem điều gì sẽ xảy ra.
Pris

2Nathan Reed: Tác giả đã viết, rằng anh ta có cảnh OpenGL, vì vậy tôi nghĩ, đó cũng có thể là vấn đề này.
zacharmarz

Vấn đề này có vẻ tương tự hoặc liên quan, nhưng các giá trị bộ đệm độ sâu chắc chắn KHÔNG được lưu trữ theo cách tương thích với các số dấu phẩy động. Nó là một định dạng điểm cố định. Chính vì điều này mà câu trả lời có thể sai lệch.
Steven Lu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.