Thuật toán tính gần đúng nghịch đảo của căn của giá trị x

Thuật toán này theo mình là một “điều kỳ diệu”. Chỉ với đúng 10 dòng code, mà trong đó, thực chất chỉ có 2 dòng thực hiện tính toán, với các phép tính đơn giản gồm phép cộng, nhân và dịch bit, thuật toán đã đưa ra một phép tính gần đúng cực kỳ hiệu quả để tính 1x. Đây là thuật toán được xem là “chìa khóa” , dùng trong game Quake 3, để mở cánh cổng đến thế giới giả lập 3D, vào thời kỳ sơ khai khi sức mạnh tính toán còn chưa đủ mạnh.

English version here

				
					float Q_rsqrt(float number)
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;                       // evil floating point bit level hacking
  i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration  

  return y;
}

				
			

Chúng ta cùng tìm hiểu thuật đoạn code trên

💡 Bạn đọc có thể xem thêm chi tiết tại: Wikipedia Single-precision floating-point format

Theo như chuẩn này, một số float được dùng 32 bit để lưu trữ làm 3 phần: S  1 bit dấu thể hiện số âm / số dương, E – 8 bit thể hiện số mũ, M – 23 bit lưu giá trị được chuẩn hóa về 223.

Như vậy, một biến x được tính bởi công thức: x=(1+M223)2E127()

Và, thể hiện trên giá trị bit của x sẽ là: M+223E()

Với x có giá trị nhỏ x[0,1), chúng ta có thể tính xấp xỉ của x thông qua công thức xấp xỉ: xlog2(1+x). Vậy:

log2(1+x)=x+μ;(μ is empirically chosen at 0.043)

Với x bất kỳ, ta có theo (*):

log2x=log2((1+M223)2E127)=log2(1+M223)+E127

Vì M223[0,1), ta áp dụng công thức xấp xỉ:

log2(1+M223)=M223+μ

Lúc đó:

log2x=M223+μ+E127=M223+223E223+μ127=1223(M+223Ebit representation (**))+μ127

 

Ý nghĩa của dòng code

				
					i  = 0x5f3759df - ( i >> 1 );

				
			

Ta có:

log2(1x)=log2(x12)=12log2(x)

Đặt Γ=1xlog2(Γ)=log2(1x)=12log2(x)

Ta thay vào 2 vế của biểu diễn bit:

1223(MΓ+223EΓ)+μ127=12(1223(Mx+223Ex)+μ127)MΓ+223EΓ=32223(127μ)0x5f3759df12(Mx+223Ex)xi>>1

Đây là bí ẩn của dòng code tính toán đầu tiên.

xnew=xoldf(x)f(x)

Đặt f(y)=1y2x, như vậy: f(y)=0y=1x. Để giải nghiệm cho f(y)=0, ta dùng phương pháp lặp Newton-Ralphson.

Đầu tiên ta đi tính đạo hàm của f(y)

f(y)=1y2xf(y)=yxy32

Vậy, áp dụng công thức, ta có:

ynew=yf(y)f(y)=yyxy32=y(32x2y2)

Và đây cũng là ý nghĩa ẩn giấu của dùng code cuối cùng:

				
					 y  = y * ( threehalfs - ( x2 * y * y ) );

				
			

Subscribe to SkyGLab

Scroll to Top