Ini hanya untuk memuaskan rasa penasaran saya sendiri.
Apakah ada implementasi ini:
float InvSqrt (float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
di Rust? Jika ada, kirim kode.
Saya mencobanya dan gagal. Saya tidak tahu cara menyandikan angka float menggunakan format integer. Ini usaha saya:
fn main() {
println!("Hello, world!");
println!("sqrt1: {}, ",sqrt2(100f64));
}
fn sqrt1(x: f64) -> f64 {
x.sqrt()
}
fn sqrt2(x: f64) -> f64 {
let mut x = x;
let xhalf = 0.5*x;
let mut i = x as i64;
println!("sqrt1: {}, ", i);
i = 0x5f375a86 as i64 - (i>>1);
x = i as f64;
x = x*(1.5f64 - xhalf*x*x);
1.0/x
}
Referensi:
1. Origin of Quake3's Fast InvSqrt () - Halaman 1
2. Memahami Root Inverse Square Cepat Quake
3. ROAST INVESTE SQUARE ROOT.pdf
4. kode sumber: q_math.c # L552-L572
union
.union
berhasil juga.memcpy
pasti bekerja, meskipun itu verbose.rsqrtss
danrsqrtps
instruksi, diperkenalkan dengan Pentium III pada tahun 1999, lebih cepat dan lebih akurat daripada kode ini. NEON ARM memilikivrsqrte
yang serupa. Dan perhitungan apa pun yang digunakan Quake III untuk ini mungkin akan dilakukan pada GPU akhir-akhir ini.Jawaban:
Ada fungsi untuk itu:
f32::to_bits
mengembalikan suatuu32
. Ada juga fungsi untuk arah lain:f32::from_bits
yang mengambilu32
argumen sebagai. Fungsi-fungsi ini lebih disukai daripadamem::transmute
yang terakhirunsafe
dan sulit untuk digunakan.Dengan itu, berikut adalah implementasi dari
InvSqrt
:( Taman bermain )
Fungsi ini mengkompilasi ke rakitan berikut pada x86-64:
Saya belum menemukan rakitan referensi (jika ada, tolong beri tahu saya!), Tetapi tampaknya cukup baik bagi saya. Saya hanya tidak yakin mengapa float dipindahkan ke
eax
hanya untuk melakukan shift dan pengurangan integer. Mungkin register SSE tidak mendukung operasi itu?dentang 9.0 dengan
-O3
mengkompilasi kode C pada dasarnya majelis yang sama . Jadi itu pertanda baik.Perlu ditunjukkan bahwa jika Anda benar-benar ingin menggunakan ini dalam praktik: tolong jangan. Seperti yang ditunjukkan Benrg dalam komentar , CPU x86 modern memiliki instruksi khusus untuk fungsi ini yang lebih cepat dan lebih akurat daripada peretasan ini. Sayangnya,
1.0 / x.sqrt()
sepertinya tidak mengoptimalkan instruksi itu . Jadi, jika Anda benar-benar membutuhkan kecepatan, menggunakan yang_mm_rsqrt_ps
intrinsik mungkin adalah cara untuk pergi. Namun, ini tidak lagi membutuhkanunsafe
kode. Saya tidak akan membahas banyak detail dalam jawaban ini, karena sebagian kecil programmer benar-benar membutuhkannya.sumber
addss
ataumulss
. Tetapi jika 96 bit xmm0 lainnya dapat diabaikan maka seseorang dapat menggunakanpsrld
instruksi tersebut. Hal yang sama berlaku untuk pengurangan integer.fast_inv_sqrt
hanyalah satu langkah iterasi Newton-Raphson untuk menemukan perkiraan yang lebih baikinv_sqrt
. Tidak ada yang tidak aman tentang bagian itu. Tipuannya ada di bagian pertama, yang menemukan perkiraan yang bagus. Itu bekerja karena ia melakukan pembagian integer oleh 2 pada bagian eksponen float, dan memangsqrt(pow(0.5,x))=pow(0.5,x/2)
movd
untuk EAX dan kembali adalah optimasi yang tidak terjawab oleh kompiler saat ini. (Dan ya, konvensi memanggil lulus / kembali skalarfloat
dalam elemen rendah dari XMM dan memungkinkan bit yang tinggi untuk menjadi sampah Tetapi catatan bahwa jika itu. Itu nol-diperpanjang, dapat dengan mudah tetap seperti itu: pergeseran kanan tidak memperkenalkan non nol elemen dan juga tidak mengurangi_mm_set_epi32(0,0,0,0x5f3759df)
, yaitumovd
beban. Anda perlumovdqa xmm1,xmm0
menyalin reg sebelumnyapsrld
. Lewati latensi dari penerusan instruksi FP ke integer dan sebaliknya disembunyikan olehmulss
latensiYang ini diimplementasikan dengan kurang dikenal
union
di Rust:Apakah beberapa tolok ukur mikro menggunakan
criterion
peti pada kotak Linux x86-64. Anehnya Rust sendirisqrt().recip()
yang tercepat. Tetapi tentu saja, setiap hasil patokan mikro harus diambil dengan sebutir garam.sumber
sqrt().inv()
adalah tercepat. Sqrt dan inv adalah instruksi tunggal hari ini, dan berjalan cukup cepat. Doom ditulis pada hari-hari ketika tidak aman untuk menganggap ada hardware floating point sama sekali, dan fungsi transendental seperti sqrt pasti akan menjadi perangkat lunak. +1 untuk tolok ukur.transmute
ternyata berbeda darito_
danfrom_bits
- Saya berharap mereka menjadi setara dengan instruksi bahkan sebelum optimasi.Anda dapat menggunakan
std::mem::transmute
untuk membuat konversi yang dibutuhkan:Anda dapat mencari contoh langsung di sini: di sini
sumber
f32::to_bits
danf32::from_bits
. Itu juga membawa maksud jelas tidak seperti transmutasi, yang kebanyakan orang mungkin melihat sebagai "sihir".unsafe
harus dihindari di sini, karena itu tidak perlu.