2009-10-22 12 views
13

Có một vấn đề tính toán toán học khác một lần nữa.tính toán phao php 2 dấu thập phân

$a = 34.56 

$b = 34.55 

$a làm một số tính toán để có được con số này

$b đang làm tròn gần 0,05 để có được con số này

những gì xảy ra là

$c = $b - $a 

cho là tôi nên -0.01, nhưng tôi gọi lại số $c là hiển thị -0.00988888888888

tôi cố gắng sử dụng n umber_format($c, 2), nhưng sản lượng là 0,00,

làm thế nào tôi có thể chắc chắn $a$b là chính xác 2 số thập phân, không có số ẩn ở phía sau.

trong php của tôi kiến ​​thức number_format chỉ có thể định dạng màn hình, nhưng giá trị không thực sự 2 chữ số thập phân,

Tôi hy vọng tôi có thể nhận được sự giúp đỡ từ đây. Điều này thực sự làm tôi thất vọng.

+0

Điều tra 'độ chính xác' trong php.ini để có nhiều thay đổi hơn, thay đổi trên toàn PHP. Tuy nhiên, lưu ý rằng bạn đang xử lý * các chữ số có nghĩa * ở đó. –

Trả lời

34

Hãy thử sprintf("%.2f", $c);

số Floating point được đại diện trong ký hiệu IEEE dựa trên sức mạnh của 2, do chấm dứt số thập phân có thể không phải là một số nhị phân kết thúc, đó là lý do tại sao bạn sẽ có được các chữ số dấu.

Theo đề xuất của Bộ biến đổi độ dài biến, nếu bạn biết độ chính xác bạn muốn và nó không thay đổi (ví dụ: khi bạn đang xử lý tiền), có thể tốt hơn là chỉ sử dụng số điểm cố định thay vì đô la

$a = 3456; 

$b = 3455; 

$c = $b - $a; 

sprintf ("%.2f", $c/100.0); 

Bằng cách này, bạn sẽ không gặp phải bất kỳ lỗi làm tròn nào nếu bạn thực hiện nhiều phép tính trước khi in.

+0

cảm ơn câu trả lời của bạn, cách duy nhất là định dạng đầu ra, không thể chỉ đặt nó thành 2 thập phân, ngoại trừ việc sử dụng String. – Shiro

+0

Nếu bạn chỉ muốn đặt nó thành 2 số thập phân, bạn nên sử dụng số điểm cố định, không phải là dấu phẩy động. Xem Câu trả lời của Bộ giải mã độ dài thay đổi. –

+0

Tôi thực sự không hiểu ý nghĩa của số "điểm cố định", bạn có thể cung cấp một số ví dụ không? Cảm ơn rất nhiều! – Shiro

12

Sử dụng round():

$c = round($b - $a, 2); 

Lưu ý: bạn cũng có thể chọn chế độ làm tròn cho phù hợp.

Edit: Ok Tôi không có được những gì sprintf() được làm điều đó number_format() không phải là:

$c = number_format($b - $a, 2); 

vs

$c = sprintf("%.2f", $b - $a); 

?

+0

Có lẽ không có gì, nhưng sprintf() là rất nhiều niềm vui hơn để sử dụng cho những kẻ như tôi đã quen với C. Cũng tiết kiệm một văn bản '$ foo = number_format ($ b - $ a, 2). "và" thay vì viết '$ foo = sprintf ("%. 2f táo! ", $ b - $ a)' - thanh lịch hơn nhiều, theo ý kiến ​​của tôi. –

+0

Đá tròn, có thể là nguyên nhân mà tất cả những gì tôi biết bây giờ, vẫn đang học hỏi. – Newb

2

Bạn đã chạy vào một trong các bẫy số điểm động; rằng chúng không thể luôn đại diện cho các phân số thập phân chính xác. Nếu bạn muốn các giá trị thập phân chính xác, bạn nên sử dụng các số nguyên và sau đó chia cho độ chính xác mà bạn muốn ở cuối.

Ví dụ: nếu bạn đang tính toán trong số tiền nổi bù lại đô la (hoặc loại tiền tệ yêu thích của bạn), bạn có thể thực sự thực hiện các phép tính của mình bằng số nguyên.

+0

bạn có thể cho ví dụ về cách số nguyên cent hoạt động không? Không thực sự có được những gì bạn có nghĩa là. Cảm ơn rất nhiều về lời bình luận của bạn. – Shiro

+0

"Integer cent" có nghĩa là, ví dụ, số tiền $ 24.95 nên được lưu trữ không phải là một dấu phẩy động 24,95, mà đúng hơn là số nguyên 2495. Số tiền $ 10,00 sẽ được lưu trữ dưới dạng số nguyên 1000. Và cứ thế. –

2

Bạn có thể rất gọn gàng bỏ qua tất cả các vấn đề này chỉ đơn giản bằng cách sử dụng thư viện bcmath.

Chỉ cần nhớ đọc tài liệu và cẩn thận cho dù bạn đang chuyển đối số dưới dạng chuỗi hoặc dưới dạng kiểu dữ liệu số.

4

tính Quê quán:

$a = 34.56; 
$b = 34.55; 
$c = $b - $a; // -0.010000000000005  

trình như mong đợi (sử dụng luôn chức năng BC để tính toán số thực, vấn đề là đối với tất cả các nền tảng C dựa!):

$a = '34.56'; 
$b = '34.55'; 
$c = bcsub($b, $a, 4); // -0.0100  
+0

Để tiếp tục lặp lại điều này. Sử dụng các chuỗi để biểu diễn dữ liệu thay vì các kiểu dữ liệu float/double. Sau đó, sử dụng các hàm BCMath để tính các biến. Để sử dụng sprintf ('% 1 $ s', bcadd ($ var1, $ var2)); Điều tương tự cũng áp dụng cho việc lưu trữ dữ liệu của cơ sở dữ liệu. http://php.net/manual/en/book.bc.php – fyrye

3

tôi cũng chạy vào vấn đề này gần đây khi thực hiện các phép tính với phao nổi. Ví dụ, tôi đã có 2 phao nổi khi trừ và định dạng, giá trị là -0,00.

$floatOne = 267.58; 
$floatTwo = 267.58; 
$result = number_format($floatOne - floatTwo, 2); 
print $result; //printed a string -0.00 

Những gì tôi đã làm là:

$result = abs($floatOne - $floatTwo);// Made the number positive 
print money_format('%i', $result); // printed the desired precision 0.00 

Trong giải pháp của tôi, tôi biết rằng floatOne sẽ không bao giờ ít hơn floatTwo. Chức năng money_format chỉ được xác định nếu hệ thống có khả năng phân cấp, Windows thì không.

1

Nếu ai đó vẫn tiếp cận trang này với các vấn đề tương tự khi phép trừ số nổi gây ra lỗi hoặc các giá trị lạ. Tôi muốn giải thích vấn đề này với chi tiết hơn một chút. Thủ phạm là số dấu phẩy động. Và các hệ điều hành khác nhau và các phiên bản khác nhau của ngôn ngữ lập trình có thể hoạt động khác nhau.

Để minh họa sự cố, tôi sẽ giải thích lý do tại sao bằng ví dụ đơn giản bên dưới.

Nó không liên quan trực tiếp đến PHP và nó không phải là lỗi. Tuy nhiên, mọi lập trình viên đều phải nhận thức được vấn đề này.

Vấn đề này thậm chí đã mất nhiều mạng sống hai thập kỷ trước. Vào ngày 25 tháng 2 năm 1991, vấn đề này trong tính toán số trôi nổi trong một pin tên lửa Patriot MIM-104 ngăn chặn nó chặn một tên lửa Scud đến ở Dhahran, Saudi Arabia, góp phần vào cái chết của 28 binh sĩ từ Quân Đoàn 14 của Quân Đoàn Hoa Kỳ.

Nhưng tại sao lại xảy ra?

Lý do là các giá trị dấu phẩy động biểu thị độ chính xác giới hạn. Vì vậy, một giá trị có thể không có cùng một chuỗi đại diện sau khi xử lý bất kỳ. Nó cũng bao gồm viết một giá trị dấu chấm động trong tập lệnh của bạn và trực tiếp in nó mà không có bất kỳ phép toán nào.

Chỉ cần một ví dụ đơn giản:

$a = '36'; 
$b = '-35.99'; 
echo ($a + $b); 

Bạn mong chờ nó để in 0.01, phải không? Nhưng nó sẽ in một câu trả lời rất lạ như 0.009999999999998

Giống như các số khác, số dấu phẩy động kép hoặc nổi được lưu trữ trong bộ nhớ dưới dạng chuỗi 0 và 1.Cách dấu phẩy động khác với số nguyên là cách chúng ta diễn giải số 0 và 1 khi chúng ta muốn xem chúng. Có nhiều tiêu chuẩn cách chúng được lưu trữ.

số Floating-point thường được đóng gói vào một dữ kiện máy tính như là bit dấu, lĩnh vực mũ và significand hoặc mantissa, từ trái sang phải ....

số thập phân không tốt trong hệ nhị phân do thiếu đủ không gian. Vì vậy, uou không thể thể hiện 1/3 chính xác như nó là 0.3333333 ..., phải không? Tại sao chúng ta không thể biểu diễn 0,01 như một số phao nhị phân là vì lý do tương tự. 1/100 là 0.00000010100011110101110000 ..... với số 10100011110101110000 lặp lại.

Nếu 0,01 được giữ ở dạng đơn giản và được cắt ngắn theo hệ thống là 01000111101011100001010 khi được dịch ngược lại, nó sẽ được đọc như 0.0099999. ... tùy thuộc vào hệ thống (64bit máy tính sẽ cho bạn độ chính xác tốt hơn nhiều so với 32 bit). Hệ điều hành quyết định trong trường hợp này cho dù in nó như nó thấy hay làm thế nào để làm cho nó theo cách dễ đọc hơn. Vì vậy, nó phụ thuộc vào máy như thế nào họ muốn đại diện cho nó. Nhưng nó có thể được bảo vệ ở cấp độ ngôn ngữ với các phương pháp khác nhau.

Nếu bạn định dạng kết quả, hãy echo number_format (0.009999999999998, 2); nó sẽ in 0,01.

Đó là vì trong trường hợp này, bạn hướng dẫn cách đọc và độ chính xác mà bạn yêu cầu.