2009-02-26 27 views
9

Tôi đã tự hỏi nếu có một cách khắc phục một vấn đề chính xác điều đó dường như là kết quả của đại diện bên trong máy tính của tôi các số dấu chấm động:Đối phó với các vấn đề chính xác trong số dấu chấm động

Vì lợi ích của rõ ràng các vấn đề được tóm tắt như sau:

// str is "4.600"; atof(str) is 4.5999999999999996 
double mw = atof(str) 

// The variables used in the columns calculation below are: 
// 
//     mw = 4.5999999999999996 
//     p = 0.2 
//     g = 0.2 
//     h = 1 (integer) 

int columns = (int) ((mw - (h * 11 * p))/((h * 11 * p) + g)) + 1; 

Trước khi đúc thành một số nguyên gõ kết quả của các cột tính toán là 1,9999999999999996; gần đến mức xa so với kết quả mong muốn là 2.0.

Mọi đề xuất được chào đón nhiều nhất.

+0

câu hỏi này đã được hỏi và trả lời trước ... chỉ cần tìm kiếm nó ... –

+0

Đọc trên Phân tích số, đó là một vấn đề lớn trong các tình huống nhất định. Có thể sử dụng các thư viện toán học thay thế (nhưng chậm hơn) như BigDecimal, vv ... – JeeBee

Trả lời

4

Một cách rất đơn giản và hiệu quả để làm tròn một số dấu chấm động đến một số nguyên:

int rounded = (int)(f + 0.5); 

Lưu ý: đây chỉ hoạt động nếu f luôn là tích cực. (thanks j random hacker)

+0

Giả sử f là dương. –

+0

Có "cột" luôn tích cực trong ứng dụng này. – AndyUK

+0

@j_random_hacker - bạn có thể sử dụng giá trị tuyệt đối, theo lý thuyết. – Moshe

1

Sử dụng số thập phân: decNumber++

+2

Điều đó có giải quyết được vấn đề 3 * (1/3) không? Hoặc chỉ có vấn đề 10 * (1/10)? – MSalters

+1

-1, vì lý do chính xác mà MSalters đưa ra. Các số thập phân rất hữu ích khi làm việc với tiền không phải vì chúng có độ chính xác cao hơn mà bởi vì các phép tính không chính xác của bạn sẽ giống hệt với tất cả mọi người. Trong tất cả các khía cạnh khác, số thập phân phải chịu các vấn đề tương tự. –

+0

Mặc dù có một số thư viện lưu trữ phân số. 4.6 sẽ là 4 + 3/5 ở một trong số đó. Chúng chỉ tách rời khi đưa ra một phép toán không thể quản lý như một phần nhỏ, như nhân với pi. –

2

Bạn có thể đọc paper để tìm thấy những gì bạn đang tìm kiếm.

Bạn có thể lấy giá trị tuyệt đối của kết quả như đã thấy here:

x = 0.2; 
y = 0.3; 
equal = (Math.abs(x - y) < 0.000001) 
11

Nếu bạn chưa đọc nó, tiêu đề của this paper thực sự chính xác. Xin vui lòng xem xét đọc nó, để tìm hiểu thêm về các nguyên tắc cơ bản của số học dấu chấm động trên các máy tính hiện đại, một số cạm bẫy, và giải thích về lý do tại sao chúng hành xử theo cách chúng thực hiện.

11

Không có vấn đề về độ chính xác.

Kết quả bạn nhận được (1.9999999999999996) khác với kết quả toán học (2) bằng lề 1E-16. Đó là khá chính xác, xem xét đầu vào của bạn "4.600".

Bạn có vấn đề làm tròn, tất nhiên. Làm tròn mặc định trong C++ là cắt ngắn; bạn muốn một cái gì đó tương tự như giải pháp của Kip. Chi tiết phụ thuộc vào miền chính xác của bạn, bạn có mong đợi round(-x)== - round(x) không?

+0

+1 để nhận biết vấn đề thực sự. –

5

Nếu độ chính xác thực sự quan trọng thì bạn nên cân nhắc sử dụng các số điểm nổi chính xác gấp đôi thay vì chỉ dấu phẩy động. Mặc dù từ câu hỏi của bạn nó xuất hiện mà bạn đã có. Tuy nhiên, bạn vẫn gặp sự cố khi kiểm tra các giá trị cụ thể. Bạn cần mã dọc theo các dòng (giả sử bạn đang kiểm tra giá trị của mình theo số không):

if (abs(value) < epsilon) 
{ 
    // Do Stuff 
} 

trong đó "epsilon" là một số giá trị nhỏ nhưng không khác.

+1

Tôi nghĩ bạn có nghĩa là "abs (computed_value - expected_value)

+1

Thật vậy - nhưng tôi đã đề cập rằng mã là một ví dụ để kiểm tra không;) – ChrisF

3

Trên máy tính, số dấu phẩy động không bao giờ chính xác.Chúng luôn là sự gần đúng. (1e-16 gần.)

Đôi khi có các bit ẩn mà bạn không thấy. Đôi khi các quy tắc cơ bản của đại số không còn áp dụng: a * b! = B * a. Đôi khi so sánh đăng ký với bộ nhớ cho thấy những khác biệt tinh tế này. Hoặc sử dụng một coprocessor toán học vs một thư viện dấu chấm động thời gian chạy. (Tôi đã làm waayyy tooo này từ lâu.)

C99 xác định: (Nhìn vào math.h)

double round(double x); 
float roundf(float x); 
long double roundl(long double x); 

.

Hoặc bạn có thể cuộn của riêng bạn:

template<class TYPE> inline int ROUND(const TYPE & x) 
{ return int((x > 0) ? (x + 0.5) : (x - 0.5)); } 

Đối với dấu chấm động tương đương, hãy thử:

template<class TYPE> inline TYPE ABS(const TYPE & t) 
{ return t>=0 ? t : - t; } 

template<class TYPE> inline bool FLOAT_EQUIVALENT(
    const TYPE & x, const TYPE & y, const TYPE & epsilon) 
{ return ABS(x-y) < epsilon; } 
Các vấn đề liên quan