29/12/2012

VẤN ĐỀ LÀM TRÒN SỐ KHI DÙNG DOUBLE

Có bao giờ bạn gặp rắc rối khi gán 0.05 vào 1 biến double:
double f = 0.05
Rồi sau đó bạn tính
floor(f*100)
Bạn mong đợi nhận được 5. Tuy nhiên kết quả bạn nhận được lại là 4.
Để tìm hiểu nguyên nhân tại sao như vậy chúng ta bắt đầu tìm hiểu từ cách thức lưu số double trong bộ nhớ ra sau.
Số double sử dụng 8 byte hay 64 bit để lưu. Trong đó:

  • 1 bit dùng để lưu dấu của số double (sign bit)
  • 11 bit dùng để lưu độ lớn của số mũ (exponent)
  • 52 bit + 1 bit có giá trị mặc định để lưu phần định trị (mantissa)
value =(-1)sign(1.b-1b-2..b-52)2x 2e-1023=(-1)sign(SUMi=1,52(b-i2-i))x 2e-1023 trong đó e là số mũ
Vậy độ chính xác (machine epsilon) khi làm tròn là 2-53 do khoảng cách giữa hai số 2n và 2n+1 là 2n-52
Phần số mũ e được biểu diễn dạng offset-binary (excess-K) K=2n-1 với n là số bit. Ở đây n = 11.
Giá trị của số = (-1)signx 2exponent - 1023x1.mantissa
Bây giờ ta hãy xem số 0.05 lưu trong bộ nhớ ra sao?
0.05 = 0.00001100110011001100110011001100110011001100110011001100... =  0.1100110011001100110011001100110011001100110011001100... x 2-4
Phần sau dấu phẩy bị cắt 52 bít là:
110011001100110011001100110011001100110011001100110
Bây giờ bạn tính lại sẽ thấy giá trị là:
0.049999999999999988897769753748434595763683319091796875
Rõ ràng số này nhân với 100 và tính floor là 4 chứ không phải là 5.
Để lấy floor chính xác bạn cần cộng thêm một lượng 10-N. Trong đó N là số chữ số bạn quan tâm.

Bài viết liên quan



Không có nhận xét nào:

Đăng nhận xét