2010-08-13 58 views
5

Tôi đang cố gắng tìm ra mức độ chính xác cho các định dạng điểm nổi khác nhau trong C (nghĩa là float, double và long double). Đây là mã tôi đang sử dụng tại thời điểm này:Cố gắng viết mã để tìm máy epsilon

#include <stdio.h> 
#define N 100000 

int main(void) 
{ 
    float max = 1.0, min = 0.0, test; 
    int i;        /* Counter for the conditional loop */ 

    for (i = 0; i < N; i++) { 
     test = (max + min)/2.0; 
     if((1.0 + test) != 1.0)   /* If too high, set max to test and try again */ 
    max = test; 
    if((1.0 + test) == 1.0)  /* If too low, set min to test and try again */ 
     min = test; 
    } 
    printf("The epsilon machine is %.50lf\n", max); 
    return 0; 
} 

Giá trị này xấp xỉ ~ 2^-64 như mong đợi. Tuy nhiên khi tôi thay đổi tốc độ để tăng gấp đôi hoặc 'dài gấp đôi' tôi nhận được cùng một câu trả lời tôi sẽ nhận được một giá trị nhỏ hơn nhưng tôi không. Có ai có ý tưởng gì không?

+2

Không nổi có mantissa 23-bit? Tại sao bạn mong đợi 2^-64? – Rup

Trả lời

2

Một đoán lý do tại sao bạn đang nhận được câu trả lời giống nhau:

if((1.0 + test) != 1.0) 

Đây 1.0 là một hằng số tăng gấp đôi, vì vậy nó thúc đẩy phao của bạn vào một đôi và thực hiện việc bổ sung như là một đôi. Bạn có thể muốn khai báo một float tạm thời ở đây để thực hiện việc bổ sung, hoặc làm cho các hằng số dạng số float đó (1.0f IIRC).

Bạn cũng có thể rơi vào vấn đề vượt quá độ chính xác trong tạm thời và có thể cần phải ép nó để lưu trữ trung gian trong bộ nhớ để giảm độ chính xác chính xác.


Đây là cách nhanh chóng để làm lại phương pháp tìm kiếm phạm vi của bạn nhưng tính toán thử nghiệm theo đúng loại. Tôi nhận được một câu trả lời đó là hơi quá lớn, mặc dù.

#include <stdio.h> 
#define N 100000 
#define TYPE float 

int main(void) 
{ 
    TYPE max = 1.0, min = 0.0, test; 
    int i; 

    for (i = 0; i < N; i++) 
    { 
     TYPE one_plus_test; 

     test = (max + min)/((TYPE)2.0); 
     one_plus_test = ((TYPE)1.0) + test; 
     if (one_plus_test == ((TYPE)1.0)) 
     { 
     min = test; 
     } 
     else 
     { 
     max = test; 
     } 
    } 
    printf("The epsilon machine is %.50lf\n", max); 
    return 0; 
} 
+0

Tôi nghĩ rằng đúc '(1.0 + kiểm tra)' để 'float' có thể sửa chữa nó, nhưng tôi không chắc chắn .. –

+0

Làm thế nào để 'đúc' nó như là một phao? Tôi sẽ thử nó và xem điều gì xảy ra – JMzance

+0

Vâng tôi đã thử điều đó nhưng nó vẫn cho tôi và giá trị epsilon 2^-64 – JMzance

2

Tôi không biết thuật toán của bạn hoạt động như thế nào. Cái này (C++) cho các câu trả lời đúng:

#include <iostream> 

template<typename T> 
int epsilon() { 
    int pow = 0; 
    T eps = 1; 
    while (eps + 1 != 1) { 
     eps /= 2; 
     --pow; 
    } 
    return pow + 1; 
} 

int main() { 
    std::cout << "Epsilon for float: 2^" << epsilon<float>() << '\n'; 
    std::cout << "Epsilon for double: 2^" << epsilon<double>() << '\n'; 
} 

này tính giá trị nhỏ như vậy mà, khi thêm vào 1, vẫn còn phân biệt 1.

Output:

Epsilon for float: 2^-23 
Epsilon for double: 2^-52 
+0

Tôi không biết bất kỳ C++ Im sợ – JMzance

+0

Nó không nên quá khó để hiểu lầm nếu bạn biết C. Mẫu chỉ cho phép tôi viết' T' và thay thế 'float' hoặc 'double' cho điều đó. Và in ấn hoạt động khác nhau, nhưng đừng lo lắng về điều đó. – Thomas

0

Một vấn đề với mã như vậy là trình biên dịch sẽ tải các biến dấu chấm động vào thanh ghi dấu phẩy động của bộ vi xử lý. Và nếu bộ vi xử lý của bạn chỉ có các thanh ghi dấu chấm động có độ chính xác gấp đôi, độ chính xác sẽ giống nhau đối với floatdouble.

Bạn sẽ cần phải tìm cách buộc trình biên dịch lưu trữ giá trị dấu phẩy động quay lại bộ nhớ giữa hai phép tính (thành một biến đúng loại). Bằng cách đó, nó phải vứt bỏ độ chính xác bổ sung của thanh ghi. Nhưng các trình biên dịch ngày nay rất thông minh trong việc tối ưu hóa mã của bạn. Vì vậy, điều này có thể khó đạt được.

+0

Hmmm thú vị, tôi có thể thử sử dụng một trình biên dịch crap! lol – JMzance

+0

Tại sao không chỉ biên dịch trong chế độ gỡ lỗi hoặc tương tự, mà không thực hiện tối ưu hóa? – PeMa

8

Điều đó tùy thuộc vào ý nghĩa của bạn về "mức độ chính xác".

Số dấu phẩy động có giá trị "thông thường" (bình thường), nhưng cũng có các số đặc biệt, số phụ bình thường.Nếu bạn muốn tìm hiểu giới hạn khác nhau, tiêu chuẩn C có hằng số được xác định trước:

#include <math.h> 
#include <stdio.h> 
#include <float.h> 

int main(void) 
{ 
    printf("%30s: %g\n", "FLT_EPSILON", FLT_EPSILON); 
    printf("%30s: %g\n", "FLT_MIN", FLT_MIN); 
    printf("%30s: %g\n", "nextafterf(0.0, 1.0)", nextafterf(0.0, 1.0)); 
    printf("%30s: %g\n", "nextafterf(1.0, 2.0)-1", (nextafterf(1.0, 2.0) - 1.0f)); 
    puts(""); 
    printf("%30s: %g\n", "DBL_EPSILON", DBL_EPSILON); 
    printf("%30s: %g\n", "DBL_MIN", DBL_MIN); 
    printf("%30s: %g\n", "nextafter(0.0, 1.0)", nextafter(0.0, 1.0)); 
    printf("%30s: %g\n", "nextafter(1.0, 2.0)-1", (nextafter(1.0, 2.0) - 1.0)); 
    puts(""); 
    printf("%30s: %Lg\n", "LDBL_EPSILON", LDBL_EPSILON); 
    printf("%30s: %Lg\n", "LDBL_MIN", LDBL_MIN); 
    printf("%30s: %Lg\n", "nextafterl(0.0, 1.0)", nextafterl(0.0, 1.0)); 
    printf("%30s: %Lg\n", "nextafterl(1.0, 2.0)-1", (nextafterl(1.0, 2.0) - 1.0)); 
    return 0; 
} 

Chương trình in trên 4 giá trị đối với từng loại:

  • sự khác biệt giữa 1 và giá trị ít nhất lớn hơn 1 trong loại đó (lOẠI_EPSILON),
  • giá trị bình thường dương tính tối thiểu trong một loại nhất định (lOẠI_MIN). Số này không bao gồm subnormal numbers,
  • giá trị dương tối thiểu trong một loại nhất định (nextafter * (0 ... )). Số này bao gồm các số phụ,
  • số tối thiểu lớn hơn 1. Số này giống như TYPE_EPSILON, nhưng được tính theo cách khác.

Tùy thuộc vào ý bạn là "chính xác", bất kỳ hoặc không có điều nào ở trên có thể hữu ích cho bạn.

Đây là kết quả của chương trình trên vào máy tính của tôi:

   FLT_EPSILON: 1.19209e-07 
        FLT_MIN: 1.17549e-38 
     nextafterf(0.0, 1.0): 1.4013e-45 
    nextafterf(1.0, 2.0)-1: 1.19209e-07 

       DBL_EPSILON: 2.22045e-16 
        DBL_MIN: 2.22507e-308 
     nextafter(0.0, 1.0): 4.94066e-324 
    nextafter(1.0, 2.0)-1: 2.22045e-16 

       LDBL_EPSILON: 1.0842e-19 
        LDBL_MIN: 3.3621e-4932 
     nextafterl(0.0, 1.0): 3.6452e-4951 
    nextafterl(1.0, 2.0)-1: 1.0842e-19 
+0

Chúc mừng, tôi vui vì C đã xây dựng điều này. Tuy nhiên, nhiệm vụ của tôi là viết mã để tìm giá trị nhỏ nhất lớn hơn một giá trị có thể được biểu diễn bằng một số thực. Số đầu tiên ở đó: 1.19209e-07 là một trong những tôi mong đợi nhưng đối với một số lý do mã của tôi không cung cấp cho tôi điều đó. MAny Cảm ơn – JMzance

+0

@Jack: OK. Sau đó, bạn nên đảm bảo rằng tất cả các số dấu phẩy động được sử dụng trong phép tính của bạn là các giá trị 'float'. Vì vậy, thay vì thử nghiệm '1.0 +! = 1.0', tôi sẽ làm:' float try = 1.0 + test; if (try! = 1.0) ', v.v. –

+0

Chúc mừng các bạn, bắt đầu đưa ra câu trả lời hợp lý ngay bây giờ – JMzance

1

tôi muốn thêm rằng bạn có thể nhận được độ chính xác cao nhất từ ​​một phép tính dấu chấm động bằng cách sử dụng long double.

Để áp dụng được cho @ giải pháp Rup của, chỉ cần thay đổi TYPE-long doubleprintf tuyên bố:

printf("The epsilon machine is %.50Lf\n", max); 

Đây là Epsilon trên máy tính của tôi sử dụng float:

0.00000005960465188081798260100185871124267578125000 

Và sử dụng long double:

0.00000000000000000005421010862427522170625011179761 

Sự khác biệt là khá đáng kể.

2

Định dạng điểm nổi IEEE 754 có thuộc tính, khi được hiểu là số nguyên bổ sung của hai có cùng chiều rộng, chúng tăng theo đơn điệu so với giá trị dương và giảm đơn điệu so với giá trị âm (xem biểu diễn nhị phân của 32 bit nổi). Họ cũng có tài sản là 0 < | f (x) | < ∞ và | f (x + 1) - f (x) | ≥ | f (x) - f (x − 1) | (trong đó f (x) là sự diễn giải lại số nguyên nói trên của x). Trong các ngôn ngữ cho phép loại xảo quyệt và luôn sử dụng IEEE 754-1985, chúng ta có thể khai thác điều này để tính toán một máy epsilon trong thời gian không đổi. Ví dụ, trong C:

typedef union { 
    long long i64; 
    double d64; 
} dbl_64; 

double machine_eps (double value) 
{ 
    dbl_64 s; 
    s.d64 = value; 
    s.i64++; 
    return s.d64 - value; 
} 

Từ https://en.wikipedia.org/wiki/Machine_epsilon

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