11

Tôi đang cố gắng để chẩn đoán và sửa chữa một lỗi mà nắm để X/Y năng suất một kết quả không ổn định khi X và Y là nhỏ:Làm thế nào để chia số chính xác gấp đôi nhỏ một cách chính xác mà không có lỗi chính xác?

enter image description here

Trong trường hợp này, cả hai cx và patharea tăng trơn tru. Tỷ lệ của chúng là một asymptote trơn tru ở số cao, nhưng thất thường đối với các số "nhỏ". Ý nghĩ đầu tiên rõ ràng là chúng ta đạt tới giới hạn độ chính xác của dấu phẩy động, nhưng chính con số thực tế lại không ở gần nó. Các kiểu "Số" ActionScript là các float nổi chính xác IEE 754, vì vậy phải có 15 chữ số thập phân chính xác (nếu tôi đọc đúng).

Một số giá trị đặc trưng của mẫu số (patharea):

0.0000000002119123 
0.0000000002137313 
0.0000000002137313 
0.0000000002155502 
0.0000000002182787 
0.0000000002200977 
0.0000000002210072 

Và tử số (cx):

0.0000000922932995 
0.0000000930474444 
0.0000000930582124 
0.0000000938123574 
0.0000000950458711 
0.0000000958000159 
0.0000000962901528 
0.0000000970442977 
0.0000000977984426 

Mỗi tăng đơn điệu, nhưng tỷ lệ này là hỗn loạn như đã thấy ở trên.

Với số lượng lớn hơn, nó sẽ lắng xuống thành hyperbola trơn tru.

Vì vậy, câu hỏi của tôi: cách chính xác để xử lý các con số rất nhỏ khi bạn cần phải phân chia cái khác là gì?

Tôi nghĩ nhân nhân tử số và/hoặc mẫu số trước 1000, nhưng không thể làm việc được.

Mã thực tế được đề cập là chức năng recalculate()here. Nó tính toán trọng tâm của một đa giác, nhưng khi đa giác là nhỏ, các trung tâm nhảy thất thường xung quanh nơi này, và có thể kết thúc một khoảng cách dài từ đa giác. Chuỗi dữ liệu ở trên là kết quả của việc di chuyển một nút của đa giác theo một hướng nhất quán (bằng tay, đó là lý do tại sao nó không hoàn toàn trơn tru).

Đây là Adobe Flex 4.5.

+0

Điều gì đã xảy ra khi bạn nhân với 1000, chia, rồi chia cho 1000? – K2xL

+0

Vâng, không có gì tốt :) Trong một hiện thân hóa thân, tôi đã kết thúc với một thương với chỉ hai chữ số chính xác. Tại thời điểm này, tôi cảm thấy như tôi đã thực sự mã hóa bởi thử và sai, do đó mục tiêu của tôi để có được một chút về giáo dục đúng cách để làm việc. –

+0

Ngoài ra - bạn sẽ nhân tử số hay mẫu số? Tôi đoán sau? –

Trả lời

19

Tôi tin rằng vấn đề rất có thể là do dòng sau trong mã của bạn:

sc = (lx*latp-lon*ly)*paint.map.scalefactor; 

Nếu đa giác của bạn là rất nhỏ, sau đó lxlon là gần như giống nhau, cũng như lylatp. Cả hai đều rất lớn so với kết quả, vì vậy bạn trừ hai số gần như bằng nhau.

Để làm được việc này, chúng ta có thể tận dụng thực tế là:

x1*y2-x2*y1 = (x2+(x1-x2))*y2 - x2*(y2+(y1-y2)) 
      = x2*y2 + (x1-x2)*y2 - x2*y2 - x2*(y2-y1) 
      = (x1-x2)*y2 - x2*(y2-y1) 

Vì vậy, hãy thử này:

dlon = lx - lon 
dlat = ly - latp 
sc = (dlon*latp-lon*dlat)*paint.map.scalefactor; 

Giá trị là về mặt toán học như nhau, nhưng các điều khoản là một trật tự của độ lớn nhỏ hơn, do đó, lỗi nên là một thứ tự độ lớn nhỏ hơn là tốt.

+2

Thiên tài. Hoàn toàn giải quyết vấn đề. Bạn xứng đáng nhiều hơn so với upvote ít ỏi của tôi và đánh dấu 'câu trả lời chính xác'. –

+0

Ngẫu nhiên, bạn có thể (hoặc ai đó) xây dựng trên ý nghĩa của bạn bằng "lỗi" không? Điểm có phải là bằng cách giảm sự khác biệt về độ lớn giữa các giá trị và sự khác biệt của chúng, chúng ta bằng cách nào đó sử dụng tốt hơn không gian giới hạn của một số dấu phẩy động 64 bit? Điều này có xảy ra ở cấp trình biên dịch hay mức chip không? –

+0

Ồ, thật ra tôi hiểu rồi. lx và latp là khoảng 100. Nhân chúng với nhau, bạn là khoảng 10.000 (hai con số đáng kể của độ chính xác thập phân bị mất). Bây giờ tôi thấy tại sao nhân với 1000 thay đổi không có gì. –

3

Jeffrey Sax đã xác định chính xác vấn đề cơ bản - mất chính xác từ kết hợp các cụm từ lớn hơn nhiều so với kết quả cuối cùng. Viết lại đề xuất loại bỏ một phần của vấn đề - dường như đủ cho trường hợp thực tế, được đưa ra phản hồi hạnh phúc.

Tuy nhiên, bạn có thể thấy rằng nếu đa giác trở lại (nhiều) nhỏ hơn và/hoặc xa hơn nguồn gốc, sự không chính xác sẽ hiển thị lại. Trong công thức viết lại, các thuật ngữ vẫn còn lớn hơn một chút so với sự khác biệt của chúng.

Hơn nữa, có một 'kết hợp lớn khác & các số có thể so sánh-với-khác-dấu hiệu' trong thuật toán. Các giá trị 'sc' khác nhau trong các chu kỳ tiếp theo của phép lặp qua các cạnh của đa giác có hiệu quả kết hợp thành một số cuối cùng (nhỏ hơn nhiều) so với cá thể sc (i). (nếu bạn có một đa giác lồi, bạn sẽ thấy rằng có một chuỗi tiếp giáp của các giá trị dương, và một chuỗi tiếp giáp của các giá trị âm, trong đa giác không lồi các âm và dương có thể được đan xen với nhau).

Thuật toán đang hoạt động hiệu quả là tính toán diện tích đa giác bằng cách thêm các vùng tam giác kéo dài bởi các cạnh và nguồn gốc, trong đó một số từ là âm (bất cứ khi nào một cạnh được di chuyển theo chiều kim đồng hồ, xem nó nguồn gốc) và một số tích cực (đi ngược chiều kim đồng hồ qua mép).

Bạn loại bỏ TẤT CẢ các vấn đề mất chính xác bằng cách xác định nguồn gốc tại một trong các góc của đa giác, nói (lx, ly) và sau đó thêm các bề mặt tam giác kéo dài bởi các cạnh và góc đó (như vậy: chuyển đổi lon sang (lon-lx) và latp thành (latp-ly) - với phần thưởng bổ sung mà bạn cần xử lý hai hình tam giác ít hơn, bởi vì rõ ràng là các cạnh liên kết tới các góc gốc được chọn không tạo ra bề mặt bằng 0. Đối với phần diện tích, tất nhiên, bạn sẽ phải "biến đổi trở lại" kết quả thành khung gốc, tức là thêm (lx, ly) ở cuối.

+0

Cảm ơn lời giải thích bổ sung này. –

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