Aihearkisto: Ohjelmointi

Perustarkkuuksisen IEEE 754 standardin mukaisen luvun muuttaminen liukuluvuksi

Tässä blogauksessa käsittelen perustarkkuuksisen IEEE 754 luvun muuttamista liukuluvuksi (reaaliluvuksi). Asian käsittely juontaa juurensa eräästä modbus-väylään liitettävästä laitteesta, jonka antamat tulokset normaaleilla muunnosoperaatiolla antoi käsittämättömiä tuloksia.

Väylältä saatu arvo piti saada muutetuksi reaaliluvuksi. Väylälaitteelta saatu arvo oli esimerkiksi 402851D8h. Kun tälle luvulle teki DWORD_TO_REAL muunnoksen, sai tulokseksi 1.076384 * 10^9, vaikka oikea arvo olisi ollut 2.6299953. Jokin siis mätti ja pahasti.

Selvisi, että ABB AC500 logiikat / CoDeSys ohjelmointikieli ei tue liukulukumuunnosta natiivisti. Muunnosta ei siis voinut tehdä DWORD_TO_REAL toimintoa käyttäen. Samalla selvisi, ettei sopivaa muunnospalikkaa ole ainakaan yleisesti saatavilla, joten sellainen oli tehtävä itse. Mutta ennen muunnospalikan ohjelmoimista, oli tiedettävä miten IEEE 754 luku ylipäätään muodostuu.

IEEE 754 on liukulukustandardi, joka on yleisin tietokoneissa käytettävä liukulukustandardi. Satandardi määrittää neljä erilaista liukulukua, erot liittyvät näyttötarkkuuteen ja lukualueen suuruuteen.

NimiEng. nimiEtumerkkiExponenttiDesimaaliosaBias
PuolitarkkuusHalf precision1 [15]5 [14-10]10 [9-0]15
PerustarkkuusSingle precision1 [31]8 [30-23]23 [22-0]127
KaksoistarkkuusDouble precision1 [63]11 [62-52]52 [51-0]1023
Quadruple precision1 [127]15 [126-112]112 [111-0]16383

Mittalaitteen antama tieto on kahden sanan pituinen, eli DWORD (32bit). Toisinsanoen mittatieto on perustakkuuksinen liukuluku. Tämä tarkoittaa, että 31. bitti kertoo luvun etumerkin, seuraavat 8 bittiä (bitit 30-23) kertovat exponentin ja loput 23 bittiä (bitit 22-0) desimaaliosan. Muut tarkkuusluokat lasketaan saman periaatteen mukaisesti, mutta niissä on vain enemmän/vähemmän bittejä ja tarkkuuluokkakohtainen bias. Allaolevaa ohjetta voi siis käyttää nämä seikat huomioonottaen myös muissa tarkkuusluokissa.

Muunnos tapahtuu seuraavasti:

  1. Muunnetaan DWORD, eli tuplasana, binäärimuotoon: 4028 51D8 h => 01000000 00101000 01010001 11011000 b
  2. Ryhmitellään edellinen bittijono kolmeen ryhmään, edelläkuvatun mukaisesti: 0 10000000 01010000101000111011000
  3. Luvun etumerkki määräytyy bitin 31 mukaan: 0 = positiivinen, 1 = negatiivinen. Koska bitti on tällä kertaa 0, on luku positiivinen.
  4. Seuraavasta ryhmästä lasketaan exponentti, joka on suora b => dec muunnos. Exponentti on siis 10000000 b => 128.
  5. Koska luku on perustarkkuuksinen, on exponentin bias 127. Täten lopulliseksi exponentiksi muodostuu 128 – 127 = 1.
  6. Seuraava vaihe, eli desimaaliosan laskenta on kaikkein työläin prosessi koko muunnoksessa. Luku muodostuu 2-kantalukuisen negatiivisien potenssien summasta, potenssi määräytyy bitin paikan mukaan (eniten merkitsevä bitti vasemmalla, vähiten merkitsevä oikealla). 01010000101000111011000 b => 0*2^-1 + 1*2^-2 + 0*2^-3 + 1*2^-4 + 0*2^-5 + 0*2^-6 … => 1/4 + 1/16 + 1/1024 … = 0.314997673034668
  7. Lopullinen tulos lasketaan kaavalla (-1)^etumerkkibitti * (1 + desimaaliosa) * 2^(exponentti – bias) => (-1)^0 *(1 + 0.314997673034668) * 2^(128 – 127) = 2.62999534607
  8. Tulos on siis noin 2.63