Ohjelmointi 1, syksy 2024, luento22

# L22

22. luento: ti 12.11.2024 klo 14:15-16:00: Liukulukujen esittäminen tietokoneessa, kertausta

Luentolinkit ovat 2023 luennolle

VL: Kiitti, korjattu

14 Nov 24 (edited 14 Nov 24)

Luennon sisältö

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ä
    1. Tehdään uusi kortti GitHubiin ja haara paikalliseen gitiin
    2. Speksataan, mitä oikeasti halutaan
    3. Tutkitaan, mitä on valmiina ja miten kytketty yhteen, TIMin (takaisin)mallinnus ja toteutuksen suunnittelu
    4. Toteutetaan yksi pala ja testataan, että toimii
    5. Tehdään commit
    6. Toistetaan vaiheet 3-5 kunnes valmis
    7. Tehdään liitospyyntö (pull request/merge request) ja annetaan katselmoitavaksi

Ylimääräisten tyhjien poistaminen

# Plugin1
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

# Plugin2
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);

    }
}

 

# summa2

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.

# floatvika2
        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.

# intdouble
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

# real

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ä

# siirra
/*
 * 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ä

# muunna
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.