Aritmatika titik tetap dalam C ++

Hari ini saya akan memberi tahu Anda apa itu fixed-point, mengapa itu diperlukan dan bagaimana bisa digunakan.

Ada masalah seperti itu ketika kinerja aplikasi terasa menurun karena kekhasan perhitungan floating point. Sebagai aturan, CPU dipertajam untuk operasi integer, dan coprocessor FPU (floating point unit) di dalamnya bekerja pada urutan besarnya lebih lambat. Ada platform seperti itu di mana tidak ada FPU sama sekali dan meniru operasi dengan angka akan memakan banyak waktu. Misalnya, di hadapan FPU, penggandaan angka floating-point dilakukan oleh hanya satu perintah fmul, dan tanpa adanya FPU, penggandaan dilakukan oleh fungsi emulasi __mulsf3. Dibandingkan dengan perintah fmul, fungsi __mulsf3 mengemulasi operasi pada angka floating-point, sedangkan perhitungan dilakukan dalam bentuk integer, yang mengarah pada peningkatan kode mesin dan waktu untuk mengeksekusinya, sedangkan perintah fmul akan melakukan operasi ini dengan cepat, menggunakan perangkat keras dana.

Ada solusi untuk masalah ini, yang memungkinkan melakukan perhitungan dengan titik tetap pada tipe integer.

Prinsip dari tipe ini adalah pergeseran tetap dari angka dengan N bit, sebagai akibatnya angka fraksional dapat direpresentasikan sebagai bilangan bulat dan akan memiliki akurasi 2 ^ N setelah titik. Contoh konversi angka floating-point ke angka fixed-point dari urutan 8 bit (2 ^ 8 = 1024).

Berikut adalah contoh konversi angka titik-mengambang ke nomor titik tetap:

Fixed(12345,6789) = 1024 * 12345,6789 = 12641975,<s>1936</s> 

Angka ini, setelah titik, memiliki akurasi 2 ^ 8 setelah titik desimal.

Contoh terjemahan terbalik dari angka titik tetap ke angka titik mengambang.

 Float(12641975) = 12641975 / 1024 = 12345,678<s>7109375</s> 

Dalam hal ini, angka setelah terjemahan balik memiliki bentuk 12345.6787109375 dan akurat 3 digit setelah periode, akurasi maksimum sebenarnya 2 ^ 8 = 1024.

Bagaimana perhitungan pada tipe titik tetap terjadi?


Operasi penjumlahan dan selisih sama dengan operasi bilangan bulat biasa.

Fixed(x) + Fixed(y) Fixed(x) - Fixed(y) , dalam urutan apa pun
(1024 * x) + (1024 * y) (1024 * x) - (1024 * y)

Penggandaan angka-angka tersebut dibuat dalam bentuk ini.
(Fixed(x) * Fixed(y)) / p , ini setara, dengan urutan 8 bit
((1024 * x) * (1024 * y)) / 1024

Divisi.
(Fixed(x) * p) / Fixed(y) , juga dengan urutan 8 bit, ini
(1024 * 1024 * x)*(1024 * y)

Overflow


Saat melakukan operasi penggandaan dan pembagian, kasus luapan mungkin terjadi, yang akan mengakibatkan hasil yang salah. Ini akan terjadi jika, misalnya, tipe integer 32-bit digunakan dan selama perhitungan akan terjadi overflow tipe ini dan sebagai akibat dari overflow ini, nomor tersebut akan kehilangan bit yang paling signifikan. Ada dua cara untuk menghilangkan kelebihan:

  • Lakukan perhitungan dalam tipe integer 64-bit.
  • Lakukan perhitungan dalam bentuk "dibongkar", misalnya, ketika mengalikan, (xi + xf) * (yi + yf) = xi * yi + xf * yf + xi * yf + yi * xf, awalan i dan f berarti seluruh bagian dan bagian setelah poin.

Kelas untuk bekerja dengan titik tetap di C ++


 #define DIGITS 1024 //  #define EPS 20 //       using namespace std; typedef signed int __int32_t; class Fixed { signed int x; Fixed(signed int a){ x = a; } public: Fixed(){ x = 0; } static Fixed fromInt(signed int val){ return Fixed(val*DIGITS); } static Fixed fromFloat(float val){ return Fixed((signed int)(val*DIGITS)); } float fixed2float(){ return ((float)x)/DIGITS; } Fixed sum(Fixed a,Fixed b){ return Fixed(a.x+bx); } Fixed diff(Fixed a,Fixed b){ return Fixed(ax-bx); } Fixed mul(Fixed a,Fixed b){ signed int c=ax*bx; if(c/bx != ax){ // Overflow! signed int i1 = ax/DIGITS; signed int i2 = bx/DIGITS; signed int f1 = (ax&(DIGITS-1)); signed int f2 = (bx&(DIGITS-1)); return Fixed((i1*i2)*DIGITS+(f1*f2)/DIGITS+i1*f2+i2*f1); }else{ return Fixed(c/DIGITS); } } Fixed div(Fixed a,Fixed b){ if(ax>(1<<21)){ // Overflow! signed int i = ax/DIGITS; signed int f = (ax&(DIGITS-1)); return Fixed(((i*DIGITS)/bx)*DIGITS+(f*DIGITS)/bx); }else{ return Fixed((ax*DIGITS)/bx); } } Fixed sqrt(Fixed k){ Fixed tmp(0); tmp.x = kx/2; signed int min = 0; signed int max = kx; Fixed quick(0); do{ tmp.x = (min+max)/2; quick = Fixed::mul(tmp,tmp); if(abs(quick.xk.x)<EPS) return Fixed(tmp); if(quick.x>kx){ max = tmp.x; }else{ min = tmp.x; } }while(true); } }; 

Source: https://habr.com/ru/post/id451922/


All Articles