2012-02-07 32 views
9

Tôi đang viết một phương pháp mở rộng để so sánh hai phao bằng cách sử dụng một số điểm thập phân (các số liệu quan trọng) để xác định xem chúng có bằng nhau thay vì sự chênh lệch hay chênh lệch phần trăm hay không. Nhìn qua các câu hỏi khác liên quan đến so sánh float tôi thấy các triển khai phức tạp. Tôi đã đơn giản hóa hay điều này có hợp lệ không?Đây có phải là một so sánh nổi hợp lệ mà chiếm một số vị trí thập phân không?

/// <summary> 
/// Determines if the float value is equal to (==) the float parameter according to the defined precision. 
/// </summary> 
/// <param name="float1">The float1.</param> 
/// <param name="float2">The float2.</param> 
/// <param name="precision">The precision. The number of digits after the decimal that will be considered when comparing.</param> 
/// <returns></returns> 
public static bool AlmostEquals(this float float1, float float2, int precision = 2) 
{ 
    return (Math.Round(float1 - float2, precision) == 0); 
} 

Lưu ý: Tôi đang tìm một so sánh về chữ số thập phân, không khoan dung. Tôi không muốn 1.000.000 đến 1.000.001.

+7

Tôi sẽ viết một nhóm Kiểm tra đơn vị để kiểm tra xem thuật toán của bạn có phù hợp với nhu cầu của bạn hay không. –

+7

Tôi thích tên phương thức này: ** AlmostEquals ** ... – gdoron

+0

Tôi đã viết một số bài kiểm tra đơn vị và họ vượt qua với các giá trị mà tôi đã cung cấp, nhưng tôi muốn lời khuyên của khán giả hiểu sâu hơn về phao thực hiện/hành vi. – Kim

Trả lời

2

Dựa trên câu trả lời @ Infact và một số ý kiến ​​trong cả câu hỏi và câu trả lời tôi đã đưa ra

public static bool AlmostEquals(this float float1, float float2, int precision = 2) 
{ 
    float epsilon = Math.Pow(10.0, -precision) 
    return (Math.Abs(float1-float2) <= epsilon); 
} 

này có benfit chấp nhận bất kỳ số nguyên, bạn có thể kiểm tra> 1,0 độ chính xác bằng cách sử dụng chính xác = -x, trong đó x là sức mạnh của 10 để kiểm tra.

Tôi cũng khuyên bạn nên tạo độ chính xác mặc định = 3, điều này sẽ cho bạn độ chính xác xuống đến một phần mười xu, theo mặc định, nếu phương pháp này được sử dụng cho tài chính.

+0

Chọn đây là câu trả lời vì nó giải quyết ý tưởng được quan tâm với số vị trí thập phân và mở rộng phương pháp để cho phép các giá trị âm cho độ chính xác. – Kim

+0

Đạo cụ để @infact cho 'return (Math.Abs ​​(float1-float2) <= precision);' – EtherDragon

+3

Đó là * NOT * working, phần C# thậm chí trích dẫn một ví dụ: http://msdn.microsoft.com/vi -us/library/75ks3aby.aspx Do mất độ chính xác [...] phương thức Round (Double, Int32) có thể không xuất hiện để làm tròn giá trị trung điểm tới giá trị chẵn gần nhất ở vị trí thập phân chữ số. Điều này được minh họa trong ví dụ sau, trong đó 2.135 được làm tròn thành 2.13 thay vì 2.14. Điều này xảy ra bởi vì nội bộ phương thức nhân giá trị bằng 10 chữ số, và phép tính nhân trong trường hợp này bị mất chính xác. –

0

Nếu người dùng muốn "so sánh hai phao sử dụng một số tập hợp các điểm thập phân (con số đáng kể)" và điều này thực sự có nghĩa là chúng ta có một chức năng

AlmostEquals (14.3XXXXXXXX, 14.3YYYYYYY, 1) == true cho tất cả XXX và YYY có thể và thông số cuối cùng là vị trí thập phân sau dấu thập phân.

có một câu trả lời đơn giản nhưng không may:

KHÔNG thể lập trình chức năng này sẽ thực hiện hợp đồng này. Có thể lập trình một cái gì đó mà thường cho kết quả chính xác, nhưng bạn không thể thấy trước khi điều này xảy ra, vì vậy chức năng này là vô giá trị.

Các giải pháp đưa ra ở đây phá vỡ đã có AlmostEquals (0.06f, 0.14f, 1) = true nhưng 0! = 1.

Tại sao? Lý do đầu tiên là độ nhạy cực đoan. Ví dụ: 0.0999999999 .... và 0.100000 ... 1 có các chữ số khác nhau ở vị trí đầu tiên, nhưng chúng gần như không thể phân biệt được trong sự khác biệt, chúng hầu như giống hệt nhau. Bất kể chức năng thần thoại nào, nó không thể cho phép ngay cả những khác biệt nhỏ trong tính toán.

Lý do thứ hai là chúng tôi muốn thực sự tính toán với các con số. Tôi đã sử dụng VC 2008 với C# để in ra các giá trị chính xác của hàm Math.pow. Đầu tiên là tham số chính xác, giá trị hex thứ hai của phao thu được và giá trị thứ hai là giá trị thập phân chính xác là .

1 3dcccccd 0,100000001490116119384765625

2 3c23d70a 0,00999999977648258209228515625

3 3a83126f 0,001000000047497451305389404296875

4 38d1b717 0,0000999999974737875163555145263671875

5 3727c5ac 0.00000999999974737875163555145263671875

6 358637bd 9.999999974752427078783512115478515625E-7

Như bạn thấy, trình tự 0,1, 0,01, 0,001, vv tạo ra con số đó là xấp xỉ tuyệt vời, nhưng là một trong hai hơi quá nhỏ hoặc quá lớn.

Điều gì sẽ xảy ra nếu chúng tôi thực thi rằng địa điểm đã cho phải có chữ số chính xác? Cho phép liệt kê 16 giá trị nhị phân cho 4 bit

0.0 
0.0625 
0.125 
0.1875 
0.25 
0.3125 
0.375 
0.4375 
0.5 
0.5625 
0.625 
0.6875 
0.75 
0.8125 
0.875 
0.9375 

16 số nhị phân khác nhau nên có thể đủ cho 10 số thập phân, nếu chúng ta muốn tính chỉ với một nơi sau dấu thập phân. Trong khi 0,5 là chính xác bằng nhau, thực thi cùng một chữ số thập phân có nghĩa là 0,4 nhu cầu 0,4375 và 0,9 nhu cầu 0,99375, giới thiệu các lỗi nghiêm trọng.

Vi phạm điều kiện đầu tiên cực kỳ nhạy cảm có nghĩa là bạn không thể làm bất cứ điều gì hợp lý với những con số như vậy. Nếu bạn biết rằng vị trí thập phân của một số có giá trị nhất định, bạn sẽ không cần phải tính toán ở vị trí đầu tiên.

CáC# tài liệu C thậm chí trích dẫn một ví dụ: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx

Thuyết Người gọi

Do mất độ chính xác có thể là kết quả của đại diện giá trị thập phân như số dấu chấm động hoặc thực hiện số học hoạt động trên các giá trị dấu phẩy động, trong một số trường hợp, phương thức Round (Double, Int32) có thể không xuất hiện để làm tròn giá trị trung điểm đến giá trị gần nhất ở các chữ số thập phân. Điều này được minh họa trong ví dụ sau , trong đó 2.135 được làm tròn thành 2,13 thay vì 2,14. Điều này xảy ra vì nội bộ phương pháp nhân giá trị bằng 10 chữ số và thao tác nhân trong trường hợp này bị mất chính xác .

+0

Tiền đề của bạn không đúng. Trong khi số điểm nổi * là * xấp xỉ, chắc chắn có thể dự đoán mức độ lỗi trong xấp xỉ đó, và miễn là bạn biết điều đó, bạn cũng biết liệu so sánh của bạn có hợp lệ hay không. –

+0

Có thể dự đoán mức độ sai số cho một chuyển đổi nhị phân nhị phân và ngược lại, nhưng do vấn đề độ phân giải cần thiết có thể nhỏ bằng số dấu phẩy động nhỏ nhất, phá vỡ mã. OP muốn rằng AlmostEquals (10.0,10.0XXXXXX, 1) == true, cho phép các số 10.0000000..1 và 10.0999999999999 .... làm đầu vào hợp lệ. Các thói quen chuyển đổi dấu chấm động chọn điểm nổi tiếp theo, nhưng có thể là 9.999999999 hoặc 11.10000001. Bây giờ với sự chăm sóc cực độ, bạn có thể lập trình các hàm tạo cho số thập phân nhỏ nhất, nhưng bạn không thể thêm vv vì nó bị hỏng. –

+0

+1 @ThorstenS. cho một câu trả lời rất tinh tế và xây dựng về vấn đề này. – XAMlMAX

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