Konversioita Julian datatyyppien välillä

Juliasta löytyy 8, 16, 32 ja 64-bittiset datatyypit kuten muistakin ohjelmointikelistä. Lisäksi löytyvät merkkijonot ja binääri- sekä heksaesitysmuodot. Käyn tässä kirjoituksessa läpi joitakin tyyppimuunnoksia jotka tulevat joissakin yhteyksissä vastaan mutta eivät ole aivan ilmeisiä.

Tarkastelen tässä hieman tavanomaisesta poikkeavia tyyppikonversioita Julian tyyppien välillä.

Kokonaislukujen konversio bittijonoksi onnistuu bitstring-komennolla:

l = UInt8(123)
0x7b
b = bitstring(l)
"01111011"

Takaisin päin päästään parse-komennolla:

l = parse(UInt8, b, base=2)
0x7b

Hieman harvemmin käytetyt operaatiot << ja >> voivat olla melko käteviä toisinaan. Ne siirtävät luvun bittejä oikealle tai vasemmalle:

l1 = bitstring(UInt8(123) << 1)
"11110110"
l2 = bitstring(UInt8(123) >> 1)
"00111101"

Näiden konversio takaisin kokonaisluvuiksi:

parse(UInt8, l1, base=2) * 1
246
parse(UInt8, l2, base=2) * 1
61

Left shift siis tuplaa kokonaisluvun ja right shift puolittaa sen, ns. integer division.

div(123, 2) == 123 >> 1
true

Ohjelmointikielissä kokonaisluku ja liukuluku ovat täysin eri asioita, vaikka niitä voikin sekoittaa keskenään. Esimerkiksi lukujen 1 ja 1.0 binääriesitykset 16-bittisella aritmetiikalla:

f1 = Float16(1.0)
l1 = Int16(1)
f1, l1
(Float16(1.0), 1)
b1 = bitstring(f1)
b2 = bitstring(l1)
b1, b2
("0011110000000000", "0000000000000001")

Jos lasketaan 1 + 1.0, pitää kokonaisluku ensin muuttaa liukuluvuksi. Nämä binääriesitykset voidaan taas parse-komennolla muuttaa 16-bittisiksi kokonaisluvuiksi:

m = parse(Int16, b1, base=2)
n = parse(Int16, b2, base=2)
m, n
(15360, 1)

Tässä m on nyt liukuluku 1.0, tosin väärässä tyypissä. Se voidaan tulkita uudelleen liukuluvuksi reinterpret-komennolla:

f2 = reinterpret(Float16, m)
Float16(1.0)

Bittitason manipulointiin päästään muuntamalla binääriesitys vaikkapa boolean-vektoriksi:

bits = [b == '1' for b in b1]
bits'
1×16 LinearAlgebra.Adjoint{Bool,Array{Bool,1}}:
 0  0  1  1  1  1  0  0  0  0  0  0  0  0  0  0

https://en.wikipedia.org/wiki/Half-precision_floating-point_format

sign 1 bit, exponent 5 bit, fraction 10 bit

ASCII-merkkijonot voidaan esittää 8-bittisillä unsigned integer tyypeillä. Näin nämä on tyypillisesti tallennettu tiedostoformaateissa.

s = Vector{UInt8}("Hello, world!")
s
13-element Array{UInt8,1}:
 0x48
 0x65
 0x6c
 0x6c
 0x6f
 0x2c
 0x20
 0x77
 0x6f
 0x72
 0x6c
 0x64
 0x21

Konversio merkkijonoksi tapahtuu String-komennolla:

String(s)
"Hello, world!"

Merkkijonon muuttaminen kokonaisluvuksi tapahtuu niin, että ensin tehdään heksadesimaaliesitys bytes2hex-funktiolla jonka jälkeen parsitaan BigInt-tyypiksi parse-komennolla:

s = Vector{UInt8}("Hello, world!")
h = bytes2hex(s)
"48656c6c6f2c20776f726c6421"
l = parse(BigInt, h, base=16)
5735816763073854953388147237921

Toiseen suuntaan mennään muuntamalla BigInt ensin heksadesimaaliesitysmuotoon jonka jälkeen hex2bytes-funktiolla takaisin Vector{UInt8}-tyypiksi josta edelleen merkkijonoksi:

l2 = string(l, base=16)
"48656c6c6f2c20776f726c6421"
l3 = hex2bytes(l2)
l3'
1×13 LinearAlgebra.Adjoint{UInt8,Array{UInt8,1}}:
 0x48  0x65  0x6c  0x6c  0x6f  0x2c  0x20  0x77  0x6f  0x72  0x6c  0x64  0x21
String(l3)
"Hello, world!"