Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

2. Reprezentace dat v paměti a plovoucí řádová čárka

Osnova cvičení

  1. základní reprezentace čísel
  2. plovoucí řádová čárka
  3. sestavovací program make
  4. Upozornění na 1. domácí úkol (https://dcenet.felk.cvut.cz/apo/)

Co bych si měl na druhé cvičení zopakovat / připravit

  1. operace sčítání, odčítání, násobení a dělení
  2. logické operace s binárními čísly (and, or, rotace, …)
  3. přečíst/zopakovat znalosti z APOLOS
  4. reprezentace reálných čísel (IEEE 754)

Náplň cvičení

Reprezentace čísel v paměti

Na cvičení budeme vycházet z následujícího programu v C, který budeme dále modifikovat.

Program pro zobrazování reprezentace čísel v paměti:

/* Simple program to examine how are different data types encoded in memory */

#include <stdio.h>

/*
 * The macro determines size of given variable and then
 * prints individual bytes of the value representation
 */
#define PRINT_MEM(a) print_mem((unsigned char*)&(a), sizeof(a))

void print_mem(unsigned char *ptr, int size) {
  int i;
  printf("address = 0x%016lx\n", (long unsigned int)ptr);

  for (i = 0; i < size; i++) {
    printf("0x%02x ", *(ptr+i)); // == printf("0x%02x ", ptr[i]);
  }

  printf("\n");
}

int main() {
  /* try for more types: long, float, double, pointer */
  unsigned int unsig = 5;
  int sig = -5;

  /* Read GNU C Library manual for conversion syntax for other types */
  /* https://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html */
  printf("value = %d\n", unsig);
  PRINT_MEM(unsig);

  printf("\nvalue = %d\n", sig);
  PRINT_MEM(sig);

  return 0;
}

Zdojový kód programu je z počítačů v laboratoři možné zkopírovat z adresáře /opt/apo/binrep/print_binrep. K překladu programu použijte buď přímo příkaz:
gcc -Wall -pedantic -o print_binrep ./print_binrep.c 
nebo přiložený soubor s popisem sestavení (Makefile) pro program 'make'. K editaci použijte některý z nainstalovaných editorů (geany, vim, emacs, qtcreator, …). Pro ty, co nemají vlastní preferenci je vhodné začít třeba s programem geany.

Alternativa v jazyce Python

#!/usr/bin/python3

import struct

a = 1234.56789
b = -11.1111111111111111111111
c = a + b

buf = struct.pack('<f', c)

print ('C float LE:' + ' '.join(["{0:02x}".format(b) for b in buf]))
print ('C float LE:' + str(struct.unpack('<f', buf)[0]))

buf = struct.pack('>f', c)

print ('C float BE:' + ' '.join(["{0:02x}".format(b) for b in buf]))
print ('C float BE:' + str(struct.unpack('>f', buf)[0]))

buf = struct.pack('<d', c)

print ('C double LE:' + ' '.join(["{0:02x}".format(b) for b in buf]))
print ('C double LE:' + str(struct.unpack('<d', buf)[0]))

buf = struct.pack('>d', c)

print ('C double BE:' + ' '.join(["{0:02x}".format(b) for b in buf]))
print ('C double BE:' + str(struct.unpack('>d', buf)[0]))

Více o ukládání proměnných v specifikovaném formátu datové reprezentace v moduly jazyka Python struct — Interpret bytes as packed binary data.

Sestavovací program make

V situaci, kdy máme jeden zdrojový soubor není problém spouštět překladač pokaždé ručně. U větších projektů ale máme souborů víc a často různých typů – máme třeba zdrojové kódy v C a v assembleru, dokumentaci v XML a třeba i něco dalšího. Překládat ručně každý soubor zvlášť a ještě k tomu jiným překladačem a spojovat výsledek dohromady je zbytečně velká práce. Proto byl vymyšlen program make. Jeho základní myšlenka je ta, že se popíše jak se překládá který typ souboru a stanoví se které soubory jsou potřeba pro “vyrobení” jiného souboru.

Program make funguje tak, že po spuštění si přečte soubor Makefile, kde jsou zapsána pravidla pro kompilaci a vyvolává postupně překladače tak, jak je potřeba.

V souboru Makefile existují čtyři typy řádek:

  • popis závislostí,
  • příkazy pro kompilaci,
  • nastavování proměnných,
  • komentáře.

Nejjednodušší Makefile pro kompilaci programu hello vypadá následovně.

hello: hello.c

Obsahuje pouze jednu řádku s popisem závislostí. Takto zapsaná řádka říká, že pro vytvoření souboru hello je potřeba soubor hello.c. Tomu, co je vlevo od dvojtečky se říká target (cíl) a vše co je napravo jsou takzvané prerekvizity.

Program make má v sobě vestavěna implicitní pravidla, díky nimž ví, jak má provést překlad. Kdyby tato pravidla neexistovala, musel by náš Makefile vypadat takto:

hello: hello.c
	gcc -Wall -o hello hello.c

Na druhé řádce, která začíná tabulátorem, je příkaz, který bude vykonán pokud make usoudí, že je potřeba překompilovat soubor hello.

Implicitní pravidla říkají, jak z jednoho typu souboru vyrobit jiný typ souboru. Tato pravidla jsou většinou definována pomocí proměnných. Chceme-li implicitní pravidlo jen trochu pozměnit, nemusíme ho definovat znova, ale stačí změnit jen hodnoty proměnných, které se v pravidlech používají.

CFLAGS = -g -Wall
CC = m68k-elf-gcc
hello: hello.c

Pomocí proměnné CFLAGS se například určuje, s jakými přepínači má být spouštěn překladač jazyka C. Jak se jmenuje překladač se zase specifikuje v proměnné CC[3].

Pokud chceme překládat větší projekt, bývá zvykem rozšířit Makefile o další targety:

all: hello

clean:
	rm -f hello

hello: hello.c
	gcc -Wall -g -o hello hello.c

O programu make by toho šlo napsat mnohem víc. Jdou pomocí něj překládat i velmi komplikované projekty jako například samotné jádro Linuxu. Dost často ale není potřeba vytvářet složité soubory Makefile ručně. Nástroje jako autoconf či nějaké IDE většinou Makefile generují automaticky.

Úkoly

  1. přeložte a spusťte uvedený program, interpretujte výstupy programu a postupně ho modifikujte tak
    • aby tiskl vnitřní reprezentaci i jiných datových typů (např. char, float, long, int*)
    • aby vytiskl tabulku celých čísel včetně jejich reprezentace v rozsahu -16 až 15
    • aby realizoval operace sčítání a odčítání dvou proměnných (celá čísla) a vytiskl na obrazovku vstupní operandy a výsledky těchto operací včetně jejich vnitřní reprezentace
    • vyzkoušejte operace s kladnými i zápornými čísly, zaměřte se i na takové hodnoty, kdy po provedení operace dojde k přetečení
  2. Vyzkoušejte si ručně sčítání a odčítání celých čísel v doplňkovém kódu
    • demonstrujte výpočet např. 7+6, 7-6
    • procvičte výpočet na dalších číslech a s pomocí programu výsledky ověřte
    • Kdy může dojít k přetečení a jak poznáte, že k němu došlo?
  3. Násobení celých čísel
    • demonstrujte výpočet např. 7*6
    • jak se výpočet změní v případě záporných čísel? např. -7*6, -7*(-6), 7*(-6)
    • rychlá verze hw násobičky (jak zrychlit opakované sčítání na jedné sčítačce použitím většího množství sčítaček?)
  4. Dělení celých čísel
    • demonstrujte výpočet např. 42/7, 43/7
    • jak se výpočet změní v případě záporných čísel?
  5. Reprezentace reálných čísel (IEEE 754)
    • binární reprezentace reálných čísel (float - 32bit, double - 64bit)
    • převeďte na binární reprezentaci číslo -0.75, ověřte správnost s pomocí programu pro zobrazení reprezentace v paměti
    • převeďte float z binární reprezentace 0xC0A00000 na reálné číslo v desitkové soustavě
    • demonstrujte výpočet (v desítkové soustavě) 9.999*10^1 + 1.1610*10^(-1), předpokládejte, že je možné uložit pouze 4 cifry čísla a 2 cifry exponentu.
      • Návod: 1) zarovnání čísel, 2) součet, 3) normalizace, 4) zaokrouhlení
    • v binární reprezentaci sečtěte čísla 0.5 a -0.4375
    • demonstrujte výpočet (v desítkové soustavě) 1.110*10^10 * 9.200*10^(-5)
    • v binární reprezentaci vynásobte čísla 0.5 a -0.4375
  6. Se cvičícím se pokuste program přepsat bez použití makrodefinice
    • získejte adresu proměnné v paměti, například unsigned char *p = (unsigned char *)&unsig
    • počet buněk, které proměnná zabírá - sizeof(unsig)
    • navrhněte algoritmus pro zobrazení v binární soustavě

Užitečné odkazy

Domácí úkoly

  • domácí úkoly 1 až 4 budou zadané a odevzdávané elektronickou formou
  • vstup k zadání a odevzdání úkolů je přes adresu https://dcenet.felk.cvut.cz/apo/
  • Na stránce “Assignments” naleznete seznam zadaných úkolů
  • Pro nácvik práce s odevzdávacím systémem je k dispozici nehodnocená varianta prvního úkolu 1st training homework, kde máte 999 pokusů. Nazapočítávají se z ní body, ale i tak ji musíte vyřešit aspoň na polovinu, tedy na 3 body, abyste směli odevzdat 1. hodnocený domácí úkol.
  • Případnými problémy s odevzdávacím systémem se obracejte na svého cvičícího nebo přímo na autora/správce systému Richarda Šustu
courses/b35apo/tutorials/02/start.txt · Last modified: 2021/02/19 12:52 by hanakmi1