2010-07-01 17 views
10

Tôi không biết tại sao Trần hoạt động như thế nào trong hình ảnh bên dướiC# Math.Có lỗi hay không?

Tại sao chế biếnFee! = Cài đặt.PaymentProcessingFeeInPercentage * awardsSum?

View image at full size

alt text http://img514.imageshack.us/img514/3950/csharpceilingproblem.png

+0

Tôi không hiểu câu hỏi của bạn, nhưng hãy chắc chắn rằng Math.Ceiling() muốn tham số thập phân hoặc đôi –

+0

Sự cố ở đây là gì? processingFee bằng với 131, có vẻ ổn. –

+0

@Serkan - Math.Ceiling chấp nhận cả số thập phân và số double. – GenericTypeTea

Trả lời

25

tỷ lệ phần trăm của bạn không phải là thực 0,05. Đó là một giá trị đóng thành 0,05 ... và có thể nhiều hơn một chút so với 0,05. Vì vậy, khi nó được nhân với 2600, bạn đang nhận được một giá trị chỉ hơn 130.0 ... mà sau đó được "trần" để 131.0.

Sử dụng một công cụ nhỏ tôi đã viết cách đây một thời gian (có sẵn từ this page về các loại điểm nổi nhị phân .NET) có vẻ như giá trị thực tế là float gần 0,05 nhất là 0,0500000007450580596923828125. Để tăng gấp đôi, đó là 0,05000000000000000277555756156289135105907917022705078125.

Đạo đức của câu chuyện: không sử dụng float cho loại điều này - sử dụng decimal. Hoặc nếu bạn chỉ đang cố gắng đại diện cho một tỷ lệ phần trăm, nếu nó là okay để thực sự chỉ chính xác đến một phần trăm, sử dụng một giá trị số nguyên 0-100.

+2

+1 để đề xuất 'Thập phân ' – Bobby

+0

sẽ hoạt động với số thập phân và thậm chí tăng gấp đôi cho tất cả các biến –

+1

@ pixel3cs: Không, không sử dụng' double'. Nó có cùng các vấn đề như 'float'. Lý do 'thập phân' tránh những vấn đề này là bởi vì nó là một số 10 điểm cơ sở thả nổi thay vì một số điểm cơ sở 2 số. Nói chung, 'decimal' sẽ là lựa chọn an toàn nhất của bạn để làm việc với tiền, bởi vì biểu diễn bên trong của' decimal' khớp với thứ bạn đang cố gắng biểu diễn (mặc dù ít hiệu quả hơn). – Brian

4

Đây là một kết quả của các đại diện dấu chấm động của những con số liên quan. See the wikipedia. Có lẽ 0.05 có vô hạn đại diện cơ sở 2 là một đôi, do đó Math.Ceiling giá trị thực sự nhìn thấy có thể là hơi lớn hơn 130.

+0

Để hoàn toàn khó chịu chính xác: một số nguyên nhỏ (toán học) như 130 có một biểu diễn hữu hạn chính xác trong các số dấu chấm động IEEE (32 bit hoặc 64 bit). Những gì có thể không thể đại diện trong số dấu chấm động là số thực (toán học) gần 130 mà OP cho là bằng 130 –

+0

@Mark: Dĩ nhiên là bạn đúng. 130 = 0.10000010 x 2^8 =) – Jens

2

Bạn đang nhìn thấy floatng điểm không chính xác.
Các thực tế cơ sở-2 đại diện của 0.05 là nhiều hơn một chút nhỏ hơn 0.05, vì vậy sản phẩm là nhiều hơn một chút nhỏ hơn 130.0.
Do đó, Math.Ceiling đang làm tròn.

Thay đổi float s và double s thành decimal.

1

Điều này là do định dạng bộ nhớ trong cho số dấu phẩy động vốn không chính xác khi số được biểu diễn bằng số thập phân. Có rất nhiều, nhiều câu hỏi về điều này trên Stack Overflow.

Số bạn đang trở về có lẽ là một cái gì đó giống như 130,000000000000001 kể từ khi các con số trong tính toán của bạn không thể được đại diện chính xác như một số dấu chấm động nhị phân.

+1

Um, 130 có thể * chắc chắn * được biểu diễn chính xác dưới dạng số dấu phẩy động nhị phân. Tuy nhiên, 0,05 thì không thể. –

+0

130 là 10000010 ở dạng nhị phân và chắc chắn có thể được biểu diễn chính xác như một điểm nổi IEEE754. –

+0

D'oh. Tất nhiên. –

1

IMHO, đây có thể là điều cần làm với độ chính xác của dấu phẩy động. Nói cách khác, 2600 × 0,05 cho 130,0000 ... 001 thay vì 130.

Nếu bạn cố gắng làm tròn kết quả trước, hãy gọi Math.Ceiling?

0

Trong trình biên dịch của tôi, khi tôi tra cứu giá trị nhân, nó nói 130.00000193715096, do đó kết quả math.ceiling là ok. Vấn đề là độ chính xác giới hạn của kiểu dữ liệu float.

Hãy thử sử dụng 'double' thay thế, nếu có thể.

+0

OP đang sử dụng gấp đôi. – maxwellb

+0

@ maxwellb: Không cho giá trị của Settings.PaymentProcessingFeeInPercentage. Tuy nhiên, 'double' vẫn sẽ trả lời" sai ". Nó chỉ đơn giản là không thích hợp để sử dụng 'float' hoặc' double' ở đây. –

+0

Ah vâng, cảm ơn. Và có vào điểm thứ hai, quá. Những gì tôi có nghĩa là "đôi là không có appropraite hơn float". – maxwellb

0

Nếu bạn sử dụng số dấu phẩy động trong một hoạt động ngân hàng lớn, đừng để tôi làm nổi tiền của tôi trong ngân hàng của bạn.Sử dụng số thập phân hoặc số nguyên của mẫu số chung nhỏ nhất, tức là xu.

Tuy nhiên, bạn có thể sử dụng Math.Round để giúp bạn sử dụng double s hoặc float s, nếu bạn đưa ra các giả định về mức độ tính toán của bạn. tức là:

double processingFee = Math.Ceiling(Math.Round( 
    Settings.PaymentProcessingFeeInPercentage * prizesSum, 2)); 
+1

Lưu ý rằng 'thập phân' trong C# vẫn là một loại dấu phẩy động. Nó chỉ là một dấu chấm phẩy * dấu * chứ không phải là dấu chấm phẩy * nhị phân *. –

+0

Vẫn "hợp lệ" chỉ cho một số lượng nhất định của sigfigs. Phân biệt tốt. – maxwellb

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