Ohjelmointi 1, syksy 2024, luento22
- M: 26.6 Liukuluku (floating-point)
- Reaalilukujen esittäminen:
- intel16.txt - reaaliluvut Intelin prosesorissa
- desimaal.c - c-ohjelma, jolla voidaa tulostaa lukujen esitysmuoto
- Tarkkuus.cs - esimerkki miten reaaliluvut eivät ole tarkkoja
- esimerkit itse ajettavana koodina
- int-, long-, float- ja double-lukujen esityskyvyn rajat
- IntDoubleYms.cs
- Luennolle tehdyt koodit versionhallinnassa -
- Luennon koodit versionhallinnassa
- 22. luento videona: MP4 alkuperäinen (TIMin kehittäminen)
- Vuoden 2019 22. luento videona: MP4 alkuperäinen (liukuluvuista tarkemmin)
- luentoseinä
Luentolinkit ovat 2023 luennolle
VL: Kiitti, korjattu
— 14 Nov 24 (edited 14 Nov 24)Luennon sisältö
- tentistä
- demoista
- TIMin kehittämisestä
- TIMin rakenne: palveludokumentaatio
- TIM ja versiohallinta: https://github.com/tim-jyu/tim
- katsotaan liukulukujen tallenusta vuoden 2019 videoilta
- videon lopussa C-kielestä ja sen käytöstä em. esimerkkien tuottamiseen.
TIMin kehittäminen
Kokeillaan toteuttaa toive: tumman teeman "pikakytkin"
Hienoa, että tumma teema on saatu lisättyä. Pidemmät sessiot tuppaavat rasittamaan silmiä ja silloin on hyvä vaihtaa tumma teema. Mietin, olisiko mahdollista lisätä yhden painikkeen taakse Esim. oikea ylänurkka "hae ja kielivalinnat en-fi-tyylisesti" painike, josta teema (ja kurssimateriaalin tausta) vaihtuisi tarvittaessa tummaksi? Varmaan on tärkeämpäkin asioita ja kokonaisuutena TiM toimii kurssin tarkoituksiin, mutta tällainen käyttäjämukavuusasia tuli mieleen.
- Strategia tiivistettynä
- Tehdään uusi kortti GitHubiin ja haara paikalliseen gitiin
- Speksataan, mitä oikeasti halutaan
- Tutkitaan, mitä on valmiina ja miten kytketty yhteen, TIMin (takaisin)mallinnus ja toteutuksen suunnittelu
- Toteutetaan yksi pala ja testataan, että toimii
- Tehdään commit
- Toistetaan vaiheet 3-5 kunnes valmis
- Tehdään liitospyyntö (pull request/merge request) ja annetaan katselmoitavaksi
Ylimääräisten tyhjien poistaminen
using System; using System.Text.RegularExpressions; /// @author Vesa Lappalainen /// @version 15.11.2011 /// <summary> /// Ylimääräisten tyhjien poistaminen reg.expillä /// </summary> public class Tyhjat { /// <summary> /// Esimerkki regexpistä /// </summary> public static void Main() { string jono = "kissa is tuu"; Regex rgx = new Regex(" +"); jono = rgx.Replace(jono, " "); Console.WriteLine(jono); } }
Esimerkki float-lukujen yhteenlaskusta
using System; /// @author Vesa Lappalainen /// @version 15.11.2011 /// <summary> /// Tutkitaan reaalilukujen tarkkuutta /// </summary> public class Tarkkuus { /// <summary> /// Esimerkki reaalilukujen epätarkkuudesta /// </summary> public static void Main() { float f = 0.1f; // 1f / 10 * 10; float s = 0; s += 0; // 1000000; Console.WriteLine("f = {0,10:F10}", f); for (int i = 0; i < 10000; i++) s += f; Console.WriteLine("s = {0,10:F10}", s); // s = 999.9029000000 Console.WriteLine("float min = " + float.MinValue); Console.WriteLine("float max = " + float.MaxValue); Console.WriteLine("double min = " + double.MinValue); Console.WriteLine("double max = " + double.MaxValue); } }
Vielä pahempi tilanne on, mikäli lähdetään lisäämään pieniä lukuja isoon lukuun. Seuraavassa esimerkissä 10 miljoonaan lisätyt luvut eivät vaikuta mitään.
float s = 10000000; // 10E7 float d = 0.1f; for (int i=0; i<1000; i++) s += d; Console.WriteLine("{0:0.00000000}",s);
Tämän takia esimerkiksi sarja pitäisi laskea aloittaen summaaminen pienimmästä luvusta.
using System; public class FloatOngelma { public static void Main() { double a, b; int c = int.MaxValue; long d = long.MaxValue; a = c; b = d; Console.WriteLine("double b:n arvo on : " + b); Console.WriteLine("doublen minimiarvo on : " + double.MinValue); Console.WriteLine("{0:n} \n{1:n} \n{2:n} \n{3:n}", a, c, b, d); float f; Console.WriteLine("floatin maksimiarvo on : " + float.MaxValue); f = c; Console.WriteLine("{0:n}", f); } }
Reaalilukujen esitys Intel
Reaalilukujen esitys
Liukuluvun esitys tietokoneessa =============================== Kymmenjärjestelmässä esimerkiksi -1 -2 -3 0.125 = 1*10 + 2*10 + 5*10 Vastaavasti voidaan tulkita 2-järjestelmässä: -1 -2 -4 0.1101 = 2 + 2 + 2 Bittien merkitykset (IEEE 754): Sign Exponent Fraction Bias Single Precision 1 [31] 8 [30-23] 23 [22-00] 127 Double Precision 1 [63] 11 [62-52] 52 [51-00] 1023 10.000: 00000000 00000000 00000000 00000000 00000000 00000000 00100100 01000000 9.000: 00000000 00000000 00000000 00000000 00000000 00000000 00100010 01000000 8.000: 00000000 00000000 00000000 00000000 00000000 00000000 00100000 01000000 7.000: 00000000 00000000 00000000 00000000 00000000 00000000 00011100 01000000 6.000: 00000000 00000000 00000000 00000000 00000000 00000000 00011000 01000000 5.000: 00000000 00000000 00000000 00000000 00000000 00000000 00010100 01000000 4.000: 00000000 00000000 00000000 00000000 00000000 00000000 00010000 01000000 3.000: 00000000 00000000 00000000 00000000 00000000 00000000 00001000 01000000 2.000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000000 1.000: 00000000 00000000 00000000 00000000 00000000 00000000 11110000 00111111 0.875: 00000000 00000000 00000000 00000000 00000000 00000000 11101100 00111111 0.800: 10011010 10011001 10011001 10011001 10011001 10011001 11101001 00111111 0.750: 00000000 00000000 00000000 00000000 00000000 00000000 11101000 00111111 0.500: 00000000 00000000 00000000 00000000 00000000 00000000 11100000 00111111 0.250: 00000000 00000000 00000000 00000000 00000000 00000000 11010000 00111111 0.100: 10011010 10011001 10011001 10011001 10011001 10011001 10111001 00111111 0.000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 -0.100: 10011010 10011001 10011001 10011001 10011001 10011001 10111001 10111111 -1.000: 00000000 00000000 00000000 00000000 00000000 00000000 11110000 10111111 -2.000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 11000000 -10.000: 00000000 00000000 00000000 00000000 00000000 00000000 00100100 11000000 10.000: 00000000 00000000 00100000 01000001 9.000: 00000000 00000000 00010000 01000001 8.000: 00000000 00000000 00000000 01000001 7.000: 00000000 00000000 11100000 01000000 6.000: 00000000 00000000 11000000 01000000 5.000: 00000000 00000000 10100000 01000000 4.000: 00000000 00000000 10000000 01000000 3.000: 00000000 00000000 01000000 01000000 2.000: 00000000 00000000 00000000 01000000 1.000: 00000000 00000000 10000000 00111111 0.875: 00000000 00000000 01100000 00111111 0.800: 11001101 11001100 01001100 00111111 0.750: 00000000 00000000 01000000 00111111 0.500: 00000000 00000000 00000000 00111111 0.250: 00000000 00000000 10000000 00111110 0.100: 11001101 11001100 11001100 00111101 0.000: 00000000 00000000 00000000 00000000 -0.100: 11001101 11001100 11001100 10111101 -1.000: 00000000 00000000 10000000 10111111 -2.000: 00000000 00000000 00000000 11000000 -10.000: 00000000 00000000 00100000 11000001 10: 00001010 00000000 9: 00001001 00000000 8: 00001000 00000000 7: 00000111 00000000 6: 00000110 00000000 5: 00000101 00000000 4: 00000100 00000000 3: 00000011 00000000 2: 00000010 00000000 1: 00000001 00000000 0: 00000000 00000000 -1: 11111111 11111111 -2: 11111110 11111111 -10: 11110110 11111111 Esimerkiksi Intelillä on vähinten merkitsevä tavu ensin, eli 1.000: 00000000 00000000 10000000 00111111 kirjoitetaan uuteen järjestykseen: e| eksp | mantissa 0 0111111 1 0000000 00000000 00000000 tästä: etumerkki: 0 (+) eksponentti: 0111111 1 - 127 = 0 mantissa: 1.0000000 00000000 00000000 eli + 1*2^0 0.875: 00000000 00000000 01100000 00111111 e| eksp | mantissa 0 0111111 0 1100000 00000000 00000000 eli + 1.11B * 2^-1 = (1 + 0.5 + 0.25)*2^-1 = 0.875 0.800: 11001101 11001100 01001100 00111111 e| eksp | mantissa 0 0111111 0 1001100 11001100 11001101 + 1.1001100... * 2^-1 ~ 0.8 Desimaaliluku voidaan muuttaa 2-järjestelmään seuraavasti: 1) Skaalaa luku välille [1,2[ kertomalla tai jakamalla toistuvasti 2:lla. Tästä saadaan luvun eksponentti, johon vielä lisätään bias (ettei tarvitse tallentaa negatiivisia lukuja). 2) Koska näin tehden mantissan ensimmäinen bitti on aina 1, sitä ei merkitä. Muut mantissan bitit saadaan kun lukua kerrotaan aina 2:lla ja kokonaisosa siirretään mantissan bitiksi. Tätä jatketaan kunnes alkuperäinen mantissa on 0 tai 2-järjestelmän mantissa on täynnä. Esimerkki -0.1 Normalisointi: 0.1 = 0.2*2^-1 = 0.4*2^-2 = 0.8*2^-3 = 1.6*2^-4 Lasketaan mantissa: Ensimmäinen bitti => 1 (jota ei merkitä) 0.6 * 2 = 1.2 => 1 0.2 * 2 = 0.4 => 0 0.4 * 2 = 0.8 => 0 0.8 * 2 = 1.6 => 1 0.6 * 2 = 1.2 => 1 josta jo huomataankin mantissan jakso Siis: Etumerkki: - => 1 Eksponentti -4 + 127 = 123 => 0111 1011 e| eksp | mantissa 1 0111101 1 1001100 11001100 11001100 (11... nostaa pyöristyksessä 1) 1 0111101 1 1001100 11001100 11001101 Eli: -0.100: 11001101 11001100 11001100 10111101
Kokeile kirjoittaa alle 32-bit float kohtaan eri lukuja:
1.000: 00111111 10000000 00000000 00000000
0.100: 00111101 11001100 11001100 11001101
0.000: 00000000 00000000 00000000 00000000
-0.100: 10111101 11001100 11001100 11001101
-1.000: 10111111 10000000 00000000 00000000
Kokonaislukujen tapauksessa kokeile vaihdella eksponentin arvoa miten saat 2, 4 jne.
https://evanw.github.io/float-toy
C-ohjelma joka tulostaa lukujen arvoja binäärisenä
/* * Ohjelmalla tulostetaan reaalilukjen bittiesityksiä. * Vesa Lappalainen 8.11.2008 * */ #include <stdio.h> #define int short // pakotetaan 16 bit "inteiksi" (Visual C) char *muutaBiteiksi(char byte,char bitit[9]) { int i; char bit; for (i=7; i>=0; i--) { bit = byte & 1; bitit[i] = '0'+bit; byte = byte >> 1; } return bitit; } void tulostaBitit(char bytes[],int n) { char bitit[9] = "00000000"; int i; for (i=0; i<n; i++) { muutaBiteiksi(bytes[i],bitit); printf(" %s",bitit); } printf("\n"); } void tulostaDouble(double d) { printf("%7.3lf:",d); tulostaBitit((char *)&d,sizeof(double)); } void tulostaFloat(float d) { printf("%7.3f:",d); tulostaBitit((char *)&d,sizeof(float)); } void tulostaInt(int d) { printf("%7d:",d); tulostaBitit((char *)&d,sizeof(int)); } #undef int int main(void) { double dluvut[] = {10,9,8,7,6,5,4,3,2,1, 0.875,0.8,0.75,0.5,0.25,0.1,0, -0.1,-1,-2,-10}; int iluvut[] = {10,9,8,7,6,5,4,3,2,1, 0,-1,-2,-10}; int dlen = sizeof(dluvut)/sizeof(dluvut[0]); int ilen = sizeof(iluvut)/sizeof(iluvut[0]); int i; for (i=0; i<dlen;i++) tulostaDouble(dluvut[i]); printf("\n"); for (i=0; i<dlen;i++) tulostaFloat(dluvut[i]); printf("\n"); for (i=0; i<ilen;i++) tulostaInt(iluvut[i]); return 0; }
C#-ohjelma joka tulostaa lukujen arvoja binäärisenä
using System; public class Muunnin { static void Main() { double[] dluvut = {10,9,8,7,6,5,4,3,2,1, 0.875,0.8,0.75,0.5,0.25,0.1,0, -0.1,-1,-2,-10}; int[] iluvut = {10,9,8,7,6,5,4,3,2,1, 0,-1,-2,-10}; foreach (double d in dluvut) Tulosta(BitConverter.GetBytes(d), $"{d, 7:0.000}"); Console.WriteLine(); foreach (double d in dluvut) Tulosta(BitConverter.GetBytes((float)d), $"{d, 7:0.000}"); Console.WriteLine(); foreach (short i in iluvut) Tulosta(BitConverter.GetBytes((short)i), $"{i, 7}"); Console.WriteLine(); } public static void Tulosta(byte[] bytes, string value) { string binaryString = ""; foreach (byte b in bytes) { string binaryByte = Convert.ToString(b, 2).PadLeft(8, '0'); binaryString += binaryByte + " "; } Console.WriteLine($"{value}: {binaryString}"); } }
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.