2012-07-06 31 views
6

thể trùng lặp:
Why can't decimal numbers be represented exactly in binary?
problem with floating valuesPHP lỗi dấu chấm động với cơ bản Toán

$var1 = 1; 

for ($i=0; $i<30; $i++) { 
    $var1 += 0.1; 
    $var2 = floor($var1); 
    $var3 = $var1-$var2; 
    if ($var3 == 0.5) { 
    $var1 = $var2+1; 
    } 
} 

Mục đích của vòng này là để đếm 1.0, 1.1, 1.2, 1.3, 1.4, và sau đó chuyển đến 2.0, 2.1, 2.2, v.v.

Vấn đề tôi nhận được là tuyên bố if không bao giờ đúng. Ngoài ra, mọi tính toán thứ mười đều giải quyết một số câu trả lời khoa học điên rồ.

Làm cách nào để khắc phục sự cố này? Hãy giúp tôi!

Chỉnh sửa: Tôi đã viết câu hỏi trong một chút vội vã thất vọng và nó đã nhiều hơn một, tôi thấy điều đó ngay bây giờ.

Phần đầu tiên của câu hỏi thực sự là "làm cách nào tôi có thể thực hiện công việc này bằng cách gửi thư ký điểm nổi" và "tại sao thư ký này lại xảy ra!"

Cảm ơn tất cả các câu trả lời tuyệt vời và tôi đang bỏ phiếu cho câu trả lời là chính xác, dễ dàng trả lời câu hỏi cốt lõi về "cách thực hiện công việc này".

Sử dụng 0,49 thay vì 0,5 và> thay vì ==. Thô lỗ và không phải là mã tốt nhất trên thế giới nhưng nó giải quyết câu hỏi ban đầu. Cảm ơn mọi người vì những phản hồi khác mà tôi sẽ đọc và theo dõi để cải thiện quá trình viết mã của tôi.

Một lần nữa, cảm ơn rất nhiều.

+0

Có thể đăng "câu trả lời khoa học điên rồ" là gì ??? – mathematician1975

+0

có thể trùng lặp của [vấn đề với các giá trị nổi] (http://stackoverflow.com/questions/6503994/problem-with-floating-values), [Hiểu các số dấu phẩy động trong php] (http://stackoverflow.com/questions/10991713/compreh-floating-point-number-in-php), v.v. – fresskoma

+0

Giải thích tuyệt vời về các vấn đề về dấu phẩy động: http://stackoverflow.com/questions/1089018/why-cant-decimal-numbers-be-represented-exactly-in-binary – mtrw

Trả lời

5

một điều này mang lại mong muốn:

$var1 = 1; 
for ($i=0; $i<30; $i++) { 
    $var1 += 0.1; 
    $var2 = (int)($var1); 
    $var3 = $var1-$var2; 
    if ($var3 >= 0.45) { 
     $var1 = $var2+1; 
    } 
    echo $var1.' '.$var2.' '.$var3.'<br/>'; 
} 
+2

Tôi đã bình chọn câu trả lời này là câu trả lời đúng, sử dụng mã của tôi, hoàn toàn là một giải pháp nhanh chóng và dễ dàng. Tôi đã bỏ phiếu cho tất cả các câu trả lời khác vì chúng giải quyết TẠI SAO. Tôi sẽ xem lại sau này nhưng tôi không có nhiều thời gian ngay bây giờ. – Dorjan

0

Các câu lệnh if có thể không bao giờ thực hiện đơn giản là do nổi điểm số học nói chung - thử nghiệm bình đẳng nghiêm ngặt với các giá trị dấu chấm động luôn mang theo nguy cơ này. Tại sao không thay đổi điều kiện để kiểm tra biến nằm trong một khoảng thời gian nhỏ trung tâm tại 0.5 để đảm bảo rằng bạn bắt nó?

0

Giữ số học số nguyên đơn giản, ngu ngốc và sử dụng.

Ví dụ bạn bắt đầu từ 1.1

Tại sao không:

kết quả
for ($i=1; $i<4; $i++) { 
    for ($j=0; $j<5; $j++) { 
    $v = $i + ($j/10); 
    # ... 
    } 
} 
+2

-1 Đối với "Ồ, xe của bạn không Vâng, sử dụng chiếc xe máy này để thay thế! " – fresskoma

+0

@ x3ro, cảm ơn vì đã cho tôi biết lý do tại sao -1 :-) Tuy nhiên, vấn đề ở đây là so sánh thập phân trên điểm nổi nhị phân. Một giải pháp hoàn toàn hợp lệ là tránh vấn đề hoàn toàn: sử dụng một hình thức algo mà không làm so sánh như vậy. Tôi sẽ đề nghị sự tương tự: Tôi không thể có được chiếc xe của tôi xuống làn đường hẹp đó - _you có một chiếc xe máy; tại sao không sử dụng nó thay thế? – TerryE

+0

Tôi sẽ +1 vì không có lý do gì để -1. Có nhiều giải pháp cho một vấn đề. Nếu một giải pháp cần rất nhiều giải pháp (trong trường hợp này, không chính xác so sánh hai phao nổi), tại sao không sử dụng giải pháp khác đơn giản hơn và có thể nhanh hơn. – invisal

3

điểm phao nhị phân sẽ chọn giá trị gần đúng để biểu diễn cơ số 10 phao điểm mà nó không thể đại diện. Vì vậy, nó không phải là chính xác để so sánh phao nổi (vì bạn không thể xác định hành vi của nó).

Bạn cần phát minh điểm nổi cơ bản-10 của riêng mình. Nó không khó để phát minh ra nếu bạn biết chính xác bạn muốn. Trong trường hợp của bạn, bạn sẽ chỉ cần một chữ số chính xác.Công thức cho số nguyên cơ số 10 của chúng tôi sẽ là: giá trị/10 cho công suất của chữ số chính xác của chúng tôi là 1, do đó công thức của bạn là = giá trị/10.

Thực hiện thao tác số học dễ dàng như thực hiện số học bình thường. Chỉ cần chuyển đổi biểu diễn bình thường (dùng để chỉ đại diện cho việc sử dụng máy tính) cho đại diện được phát minh của chúng ta. Trong trường hợp của bạn, $var1 += 0.1 sẽ chuyển sang $var1 += 1.

Xuất sẽ dễ dàng như chuyển đổi biểu diễn của bạn thành biểu diễn bình thường. Trong trường hợp của bạn, echo $var1 . '<br>'; sẽ chuyển sang echo $var1/10 . '<br>';

$var1 = 10; 

for ($i=0; $i<30; $i++) { 
    echo $var1/10 . '<br>'; 
    $var1 += 1; 
    if ($var1 % 10 == 5) { 
    $var1 = $var1+5; 
    } 
} 
1

Một giải pháp thích hợp là sử dụng bcmath gói:

$var1 = '1.0'; bcscale(1); 

for ($i = 0; $i < 30; $i++) { 
    $var2 = $var1; 
    for($j = 0; $j < 5; $j++) { 
     echo $var2 . "\n"; 
     $var2 = bcadd($var2, '0.1'); 
    } 
    $var1 = bcadd($var1, '1'); 
} 

outputs the correct result này.

2

Rất nhiều thông tin tốt về lý do tại sao điều này xảy ra và một số giải pháp. Một trong những mục tiêu thiết kế cơ bản của PHP là thiết kế kiểu lỏng lẻo của nó và đúc ngầm. Trong tinh thần đó, tôi nghĩ một trong những giải pháp đơn giản nhất cho vấn đề này là sử dụng so sánh chuỗi. Trong trường hợp của bạn, đó là giải pháp tuyệt vời:

<?php 
$var1 = 1; 

for ($i=0; $i<30; $i++) { 
    $var1 += 0.1; 
    $var2 = floor($var1); 
    $var3 = $var1 - $var2; 
    echo "$var1 | $var2 | $var3 \n"; 
    if (number_format($var3, 1) == '0.5') { 
    echo "It's true\n"; 
    $var1 = $var2 + 1; 
    } 
} 
Các vấn đề liên quan