Obsah
1. Převody zdrojových kódů z jazyka C do jazyka Go
2. Instalace transpřekladače c4go, příprava projektového souboru
3. Program typu „Hello world“ v C s jeho překladem do jazyka Go
4. Program psaný v C, jenž pracuje s řetězci
5. Transpilace zdrojového kódu do Go
6. Program psaný v C, jenž modifikuje řetězce
7. Transpilace zdrojového kódu do Go
8. Algoritmus CORDIC implementovaný v jazyku C
9. Automatická transpilace programu do Go
10. Ruční oprava některých chyb vzniklých při transpilaci
11. Porovnání výsledků: originální C program a opravený program napsaný v Go
12. Složitější program v C: výpočty algoritmu CORDIC ve fixed point aritmetice
13. Automatická transpilace programu do jazyka Go
14. Ruční oprava některých chyb vzniklých při transpilaci
15. Porovnání výsledků výpočtů
16. Program psaný v C, který vytváří a manipuluje se stromovou datovou strukturou
17. Výsledek transpilace programu do jazyka Go
19. Repositář s demonstračními příklady
1. Převody zdrojových kódů z jazyka C do jazyka Go
V dnešním článku se seznámíme s poněkud neobvyklým, ovšem potenciálně velmi užitečným projektem. Tento projekt se jmenuje c4go a jak již jeho název může napovědět, jedná se o nástroj, který je určený k převodům (transpřekladu) zdrojových kódů napsaných původně v jazyku C do jazyka Go. V úvodní větě jsem napsal, že se jedná o poněkud neobvyklý projekt. Je tomu tak z toho důvodu, že existuje takřka nepřeberné množství transpřekladačů provádějících převody do jazyka C a nikoli naopak (to je logické, protože C je v tomto kontextu chápáno jako „přenositelný assembler“ se všemi z toho plynoucími výhodami a nevýhodami). Proč však provádět opačný překlad? V C je napsáno a především odladěno velké množství algoritmů, knihoven (například knihoven pro GUI) atd. A v některých případech může být výhodné mít tyto zdrojové kódy k dispozici i pro jazyk Go, a to v „nativní“ podobě (tedy nikoli tak, že se jedná o nějaké rozhraní volající nativní céčkový kód).
2. Instalace transpřekladače c4go, příprava projektového souboru
Instalace transpřekladače c4go je snadná. Nejdříve si ovšem připravíme adresář s projektovým souborem, z něhož posléze příkaz pro instalaci spustíme:
$ mkdir c4go-test $ cd c4go-test $ go mod init c4go-test go: creating new go.mod: module c4go-test
Dále stáhneme balíček s c4go stáhneme, včetně všech pomocných balíčků:
$ go get -u github.com/Konstantin8105/c4go go: downloading github.com/Konstantin8105/errors v0.0.0-20190517083224-0667bfd7a9ac go: downloading github.com/Konstantin8105/errors v0.1.0 go: downloading github.com/Konstantin8105/tree v0.0.0-20190515202740-8a8e3df34a3b go: downloading github.com/Konstantin8105/tree v0.1.0 go: added github.com/Konstantin8105/c4go v0.0.0-20211115111653-1c67b1543446 go: added github.com/Konstantin8105/errors v0.1.0 go: added github.com/Konstantin8105/tree v0.1.0
Nainstalujeme spustitelný transpřekladač, nastavíme cestu a otestuje, zda je překladač nalezen systémem (resp. shellem):
$ go install github.com/Konstantin8105/c4go $ export PATH=~/go/bin:$PATH $ whereis c4go c4go: /tester/go/bin/c4go
Nakonec otestujeme, jestli je možné transpřekladač skutečně spustit:
$ c4go
Měly by se zobrazit tyto řádky:
Usage: c4go [<command>] [<flags>] file1.c ... Commands: transpile transpile an input C source file or files to Go ast print AST before translated Go code debug add debug information in C source version print version of c4go unused show and action for unused functions
3. Program typu „Hello world“ v C s jeho překladem do jazyka Go
Transpřekladač si nejprve otestujeme na triviálním programu – na čem jiném, než na programu typu „Hello world“, jehož céčková podoba může vypadat následovně:
#include <stdio.h> int main(void) { puts("Hello world"); return 0; }
Transpřeklad se spustí příkazem:
$ c4go transpile hello.c
V případě, že se zobrazí následující chybové hlášení, je nutné doinstalovat i balíček clang:
Error: error in function Start: preprocess error : preprocess for file: [hello.c] failed: exec: "clang": executable file not found in $PATH StdErr =
Pokud se žádné chybové hlášení nezobrazí, měl by vypadat výsledek transpřekladu následovně:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // package main import "github.com/Konstantin8105/c4go/noarch" // main - transpiled function from /root/c4go-test/hello.c:3 func main() { noarch.Puts([]byte("Hello world!\x00")) return }
Povšimněte si volání funkce Puts z balíčku noarch, reprezentace řetězce řezem bajtů, ale i toho, že konec řetězce (ukončující nula) je explicitně zapsán, aby se výsledek co nejvíce podobal céčkovému vzoru.
4. Program psaný v C, jenž pracuje s řetězci
Podívejme se nyní na jednoduchý program zapsaný v jazyku C, jenž pracuje s řetězci. Jedná se o triviální program, který napřed alokuje paměť pro řetězec, následně do alokovaného bloku překopíruje první slovo a k němu připojí slovo druhé. Výsledný řetězec je posléze vypsán na standardní výstup:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *s = (char*)malloc(100*sizeof(char)); strcpy(s, "Hello "); strcat(s, "world!"); puts(s); free(s); }
5. Automatická transpilace programu do Go
Po transpilaci zdrojového kódu z předchozí kapitoly získáme tento kód reprezentovaný v Go:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // package main import "github.com/Konstantin8105/c4go/noarch" // main - transpiled function from /root/strings.c:5 func main() { defer noarch.AtexitRun() var s []byte = make([]byte, 100*uint32(1)) noarch.Strcpy(s, []byte("Hello \x00")) noarch.Strcat(s, []byte("world!\x00")) noarch.Puts(s) _ = s }
Povšimněte si, že alokace paměti pro „řetězec“ je realizována vytvořením řezu, jenž ovšem obsahuje 100 bajtů (nenechte se zmást převodem konstanty na uint32, skutečně se jedná o sto bajtů – a nikoli o sto znaků z pohledu jazyka Go). Následně můžeme vidět volání funkcí z balíčku noarch i to, že se explicitně pracuje s nulou na konci řetězce.
6. Program psaný v C, jenž modifikuje řetězce
Předchozí program nyní nepatrně upravíme, a to konkrétně takovým způsobem, aby byl obsah řetězce programově modifikován. Modifikace (mutation) řetězce je operace, kterou lze v jazyce C bez problémů provést, ovšem v jazyce Go to není možné, protože řetězce zde nejsou modifikovatelné. To však (jak ostatně uvidíme v další kapitole) nevadí, protože c4go generuje kód pracující s řezy a nikoli s řetězci jazyka Go:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *s = (char*)malloc(100*sizeof(char)); strcpy(s, "Hello*"); strcat(s, "world!"); puts(s); s[5] = ' '; puts(s); free(s); }
Výsledkem by po překladu a spuštění tohoto programu měla být opět zpráva „Hello world!“, protože hvězdička je nahrazena za mezeru.
7. Transpilace zdrojového kódu do Go
Céčkový program popsaný v rámci předchozí kapitoly se transpřeloží (pěkné slovo, že) do následující podoby:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // package main import "github.com/Konstantin8105/c4go/noarch" // main - transpiled function from /root/strings2.c:5 func main() { defer noarch.AtexitRun() var s []byte = make([]byte, 100*uint32(1)) noarch.Strcpy(s, []byte("Hello*\x00")) noarch.Strcat(s, []byte("world!\x00")) noarch.Puts(s) s[5] = ' ' noarch.Puts(s) _ = s }
Vzhledem k tomu, že se nepracuje přímo s řetězci, ale s řezy (hodnot typu uint32), bylo možné přímo zapsat příkaz:
s[5] = ' '
Zápis ' ' přitom v jazyce Go reprezentuje runu s kódem (hodnotou) 32. Samotná runa je přitom obecně datovým typem rune, který je aliasem pro typ int32. Výše uvedené přiřazení runy do řezu bajtů bude plně funkční pouze za předpokladu, že uvnitř jednoduchých uvozovek bude použit jen ASCII znak, nikoli jiný znak. Tento možná poněkud matoucí koncept jednoznakových literálů je popsán na této stránce.
8. Algoritmus CORDIC implementovaný v jazyku C
Další program vytvořený v jazyku C, který budeme převádět do jazyka Go, je již poněkud složitější. Jedná se konkrétně o implementaci výpočtů goniometrických funkcí sinus a kosinus s využitím známého iteračního algoritmu CORDIC (Coordinate Rotation DIgital Computer). Výpočty jsou v tomto konkrétním případě realizovány s numerickými hodnotami typu double, takže převodem do Go otestujeme, jak dobře či špatně se převádí numerické výpočty, operace s poli, programové smyčky, předávání parametrů odkazem (referencí) atd.:
// -------------------------------------------------------- // Výpočet hodnot funkcí sin() a cos() pomocí iteračního // algoritmu CORDIC. // -------------------------------------------------------- #include <stdio.h> #include <math.h> #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // maximální počet iterací při běhu algoritmu #define MAX_ITER 10 // "zesílení" při rotacích #define K 0.6073 // tabulka arkustangentu úhlů double atans[MAX_ITER]; // tabulka záporných celočíselných mocnin hodnoty 2 double pows[MAX_ITER]; // naplnění tabulek atans[] a pows[] void createTables(void) { int i; for (i = 0; i < MAX_ITER; i++) { double p = pow(2.0, -i); atans[i] = atan(p); pows[i] = p; } } // výpočet funkcí sin() a cos() pro zadaný úhel delta void sincos(double delta, double *sinval, double *cosval) { int i; double x0 = 1.0; // nastavení počátečních podmínek double y0 = 0.0; double xn; for (i = 0; i < MAX_ITER; i++) { // iterační smyčka if (delta < 0) { // úhel je záporný => rotace doleva xn = x0 + y0 * pows[i]; y0 -= x0 * pows[i]; delta += atans[i]; } else { // úhel je kladný => rotace doprava xn = x0 - y0 * pows[i]; y0 += x0 * pows[i]; delta -= atans[i]; } x0 = xn; } *sinval = y0 * K; // opravit "zesílení" výsledku *cosval = x0 * K; } int main(void) { int i; createTables(); for (i = 0; i <= 90; i++) { // výpočetní smyčka double delta; // úhel, ze kterého se počítá sin a cos double sinval; // vypočtené hodnoty double cosval; double sinerr; // absolutní chyby double coserr; delta = i * M_PI / 180.0; // převod úhlu na radiány sincos(delta, &sinval, &cosval); // výpočet sinu a kosinu sinerr = fabs(sinval - sin(delta)); // výpočet absolutních chyb coserr = fabs(cosval - cos(delta)); // tisk výsledků printf ("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n", i, sinval, cosval, sinerr, coserr, 100.0 * sinerr / sinval, 100.0 * coserr / cosval); } return 0; } // finito
9. Automatická transpilace programu do Go
Po transpilaci programu, jehož zdrojový kód byl ukázán v předchozí kapitole, vznikne kód v jazyce Go, který je možné přeložit a spustit, ale který obsahuje poznámky o interních problémech nalezených transpřekladačem c4go:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ package main import "github.com/Konstantin8105/c4go/noarch" import "unsafe" import "math" // atans - transpiled function from /root/fp.c:20 // -------------------------------------------------------- // Výpočet hodnot funkcí sin() a cos() pomocí iteračního // algoritmu CORDIC. // -------------------------------------------------------- // maximální počet iterací při běhu algoritmu // "zesílení" při rotacích // tabulka arkustangentu úhlů var /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */atans []float64 = make([]float64, 10) // pows - transpiled function from /root/fp.c:23 // tabulka záporných celočíselných mocnin hodnoty 2 var pows []float64 = make([]float64, 10) // createTables - transpiled function from /root/fp.c:26 func createTables() { // naplnění tabulek atans[] a pows[] var i int32 for i = 0; i < 10; i++ { var p float64 = math.Pow(2, float64(-i)) atans[i] = math.Atan(p) pows[i] = p } } // sincos - transpiled function from /root/fp.c:37 func sincos(delta float64, sinval []float64, cosval []float64) { // výpočet funkcí sin() a cos() pro zadaný úhel delta var i int32 // nastavení počátečních podmínek var x0 float64 = 1 var y0 float64 var xn float64 { // iterační smyčka for i = 0; i < 10; i++ { if delta < 0 { // úhel je záporný => rotace doleva xn = x0 + y0*pows[i] y0 -= x0 * pows[i] delta += atans[i] } else { // úhel je kladný => rotace doprava xn = x0 - y0*pows[i] y0 += x0 * pows[i] delta -= atans[i] } x0 = xn } } // opravit "zesílení" výsledku sinval[0] = y0 * 0.6073 cosval[0] = x0 * 0.6073 } // main - transpiled function from /root/fp.c:59 func main() { var i int32 createTables() { // výpočetní smyčka for i = 0; i <= 90; i++ { // úhel, ze kterého se počítá sin a cos var delta float64 // vypočtené hodnoty var sinval float64 var cosval float64 // absolutní chyby var sinerr float64 var coserr float64 // převod úhlu na radiány delta = float64(i) * 3.141592653589793 / 180 // výpočet sinu a kosinu sincos(delta, c4goUnsafeConvert_float64(&sinval), c4goUnsafeConvert_float64(&cosval)) // výpočet absolutních chyb sinerr = math.Abs(sinval - math.Sin(delta)) coserr = math.Abs(cosval - math.Cos(delta)) // tisk výsledků noarch.Printf([]byte("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n\x00"), i, sinval, cosval, sinerr, coserr, 100*sinerr/sinval, 100*coserr/cosval) } } return } // c4goUnsafeConvert_float64 : created by c4go func c4goUnsafeConvert_float64(c4go_name *float64) []float64 { return (*[1000000]float64)(unsafe.Pointer(c4go_name))[:] } // finito
Povšimněte si toho, že parametry do funkce sincos se nepředávají čistě referencí, ale přes řezy (slice):
func sincos(delta float64, sinval []float64, cosval []float64) { ... ... ... }
Vytvoření takového řezu je vyřešeno dosti nešťastným způsobem, konkrétně vytvořením beztypového ukazatele (unsafe.Pointer:
// c4goUnsafeConvert_float64 : created by c4go func c4goUnsafeConvert_float64(c4go_name *float64) []float64 { return (*[1000000]float64)(unsafe.Pointer(c4go_name))[:] }
10. Ruční oprava některých chyb vzniklých při transpilaci
Program vzniklý automatickou transpilací je ovšem možné relativně snadno opravit takovým způsobem, aby byl čitelnější, nepoužíval unsafe ukazatele atd. Nejdříve změníme hlavičku funkce sincos z této verze:
func sincos(delta float64, sinval []float64, cosval []float64) { ... ... ... }
na následující variantu s typovanými ukazateli:
func sincos(delta float64, sinval *float64, cosval *float64) { ... ... ... }
tuto funkci lze zavolat triviálně, tedy bez použití různých triků a konverzí:
var sinval float64 var cosval float64 sincos(delta, &sinval, &cosval)
Dále odstraníme volání funkcí z balíčku noarch a použijeme standardní funkci fmt.Printf. Balíček noarch tedy ani nemusíme importovat:
fmt.Printf("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n", i, sinval, cosval, sinerr, coserr, 100*sinerr/sinval, 100*coserr/cosval)
Výsledek (po smazání různých varovných komentářů) bude vypadat následovně – jedná se o dobře čitelný Go kód:
package main import "math" import "fmt" // -------------------------------------------------------- // Výpočet hodnot funkcí sin() a cos() pomocí iteračního // algoritmu CORDIC. // -------------------------------------------------------- // maximální počet iterací při běhu algoritmu // "zesílení" při rotacích // tabulka arkustangentu úhlů var atans []float64 = make([]float64, 10) // pows - transpiled function from /root/fp.c:23 // tabulka záporných celočíselných mocnin hodnoty 2 var pows []float64 = make([]float64, 10) // createTables - transpiled function from /root/fp.c:26 func createTables() { // naplnění tabulek atans[] a pows[] var i int32 for i = 0; i < 10; i++ { var p float64 = math.Pow(2, float64(-i)) atans[i] = math.Atan(p) pows[i] = p } } // sincos - transpiled function from /root/fp.c:37 func sincos(delta float64, sinval *float64, cosval *float64) { // výpočet funkcí sin() a cos() pro zadaný úhel delta var i int32 // nastavení počátečních podmínek var x0 float64 = 1 var y0 float64 var xn float64 { // iterační smyčka for i = 0; i < 10; i++ { if delta < 0 { // úhel je záporný => rotace doleva xn = x0 + y0*pows[i] y0 -= x0 * pows[i] delta += atans[i] } else { // úhel je kladný => rotace doprava xn = x0 - y0*pows[i] y0 += x0 * pows[i] delta -= atans[i] } x0 = xn } } // opravit "zesílení" výsledku *sinval = y0 * 0.6073 *cosval = x0 * 0.6073 } // main - transpiled function from /root/fp.c:59 func main() { var i int32 createTables() { // výpočetní smyčka for i = 0; i <= 90; i++ { // úhel, ze kterého se počítá sin a cos var delta float64 // vypočtené hodnoty var sinval float64 var cosval float64 // absolutní chyby var sinerr float64 var coserr float64 // převod úhlu na radiány delta = float64(i) * 3.141592653589793 / 180 // výpočet sinu a kosinu sincos(delta, &sinval, &cosval) // výpočet absolutních chyb sinerr = math.Abs(sinval - math.Sin(delta)) coserr = math.Abs(cosval - math.Cos(delta)) // tisk výsledků fmt.Printf("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n", i, sinval, cosval, sinerr, coserr, 100*sinerr/sinval, 100*coserr/cosval) } } return } // finito
Rozdíl mezi transpilovaným kódem a ručně upraveným kódem:
--- fp_transpiled.go 2022-11-08 17:38:33.000000000 +0100 +++ fp_corrected.go 2022-11-12 14:37:22.383210131 +0100 @@ -1,46 +1,7 @@ -// -// Package - transpiled by c4go -// -// If you have found any issues, please raise an issue at: -// https://github.com/Konstantin8105/c4go/ -// - -/* AST Error : -unknown node type: `value: Int 0` -*/ -/* AST Error : -unknown node type: `value: Int 1` -*/ -/* AST Error : -unknown node type: `value: Int 2` -*/ -/* AST Error : -unknown node type: `value: Int 3` -*/ -/* AST Error : -unknown node type: `value: Int 4` -*/ -/* AST Error : -unknown node type: `value: Int 0` -*/ -/* AST Error : -unknown node type: `value: Int 1` -*/ -/* AST Error : -unknown node type: `value: Int 2` -*/ -/* AST Error : -unknown node type: `value: Int 3` -*/ -/* AST Error : -unknown node type: `value: Int 4` -*/ - package main -import "github.com/Konstantin8105/c4go/noarch" -import "unsafe" import "math" +import "fmt" // atans - transpiled function from /root/fp.c:20 // -------------------------------------------------------- @@ -50,27 +11,7 @@ // maximální počet iterací při běhu algoritmu // "zesílení" při rotacích // tabulka arkustangentu úhlů -var /* AST Error : -unknown node type: `value: Int 0` -*/ /* AST Error : -unknown node type: `value: Int 1` -*/ /* AST Error : -unknown node type: `value: Int 2` -*/ /* AST Error : -unknown node type: `value: Int 3` -*/ /* AST Error : -unknown node type: `value: Int 4` -*/ /* AST Error : -unknown node type: `value: Int 0` -*/ /* AST Error : -unknown node type: `value: Int 1` -*/ /* AST Error : -unknown node type: `value: Int 2` -*/ /* AST Error : -unknown node type: `value: Int 3` -*/ /* AST Error : -unknown node type: `value: Int 4` -*/atans []float64 = make([]float64, 10) +var atans []float64 = make([]float64, 10) // pows - transpiled function from /root/fp.c:23 // tabulka záporných celočíselných mocnin hodnoty 2 @@ -88,7 +29,7 @@ } // sincos - transpiled function from /root/fp.c:37 -func sincos(delta float64, sinval []float64, cosval []float64) { +func sincos(delta float64, sinval *float64, cosval *float64) { // výpočet funkcí sin() a cos() pro zadaný úhel delta var i int32 // nastavení počátečních podmínek @@ -113,8 +54,8 @@ } } // opravit "zesílení" výsledku - sinval[0] = y0 * 0.6073 - cosval[0] = x0 * 0.6073 + *sinval = y0 * 0.6073 + *cosval = x0 * 0.6073 } // main - transpiled function from /root/fp.c:59 @@ -135,20 +76,15 @@ // převod úhlu na radiány delta = float64(i) * 3.141592653589793 / 180 // výpočet sinu a kosinu - sincos(delta, c4goUnsafeConvert_float64(&sinval), c4goUnsafeConvert_float64(&cosval)) + sincos(delta, &sinval, &cosval) // výpočet absolutních chyb sinerr = math.Abs(sinval - math.Sin(delta)) coserr = math.Abs(cosval - math.Cos(delta)) // tisk výsledků - noarch.Printf([]byte("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n\x00"), i, sinval, cosval, sinerr, coserr, 100*sinerr/sinval, 100*coserr/cosval) + fmt.Printf("%02d\t%12.10f\t%12.10f\t%12.10f\t%12.10f\t%8.3f%%\t%8.3f%%\n", i, sinval, cosval, sinerr, coserr, 100*sinerr/sinval, 100*coserr/cosval) } } return } -// c4goUnsafeConvert_float64 : created by c4go -func c4goUnsafeConvert_float64(c4go_name *float64) []float64 { - return (*[1000000]float64)(unsafe.Pointer(c4go_name))[:] -} - // finito
11. Porovnání výsledků: originální C program a opravený program napsaný v Go
Oba programy, tj. jak originální program zapsaný v jazyku C, tak i program transpilovaný do jazyka Go, po svém překladu a spuštění zobrazí naprosto shodnou tabulku výsledky, která vypadá takto (hlavičky sloupců jsem pro větší přehled dopsal ručně). Výsledky jsou naprosto totožné až na poslední desetinné místo (což může být u některých algoritmů velmi důležité kritérium):
i sin(x) cos(x) abs.err sin(x) abs.err cos(x) rel.err sin(x) rel.err cos(x) 00 0.0011726802 1.0000761814 0.0011726802 0.0000761814 100.000% 0.008% 01 0.0167806202 0.9999360752 0.0006717863 0.0000883801 4.003% 0.009% 02 0.0363058568 0.9994176447 0.0014063601 0.0000268177 3.874% 0.003% 03 0.0519144682 0.9987285075 0.0004214880 0.0000989728 0.812% 0.010% 04 0.0714093909 0.9975241564 0.0016529171 0.0000398938 2.315% 0.004% 05 0.0858859660 0.9963821278 0.0012697767 0.0001874297 1.478% 0.019% 06 0.1053286152 0.9945147694 0.0008001519 0.0000071260 0.760% 0.001% 07 0.1208522102 0.9927479474 0.0010171332 0.0002017957 0.842% 0.020% 08 0.1401999641 0.9902008452 0.0010268631 0.0000672235 0.732% 0.007% 09 0.1556537948 0.9878894877 0.0007806702 0.0002011471 0.502% 0.020% 10 0.1749154013 0.9846615389 0.0012672237 0.0001462141 0.724% 0.015% 11 0.1902784482 0.9818084619 0.0005305471 0.0001812785 0.279% 0.018% 12 0.2092807371 0.9779342089 0.0013690463 0.0002133919 0.654% 0.022% 13 0.2245344811 0.9745450275 0.0004165732 0.0001749627 0.186% 0.018% 14 0.2435223655 0.9699745364 0.0016004699 0.0003211899 0.657% 0.033% 15 0.2586475676 0.9660513338 0.0001714775 0.0001255075 0.066% 0.013% 16 0.2774481762 0.9608206145 0.0018108204 0.0004410814 0.653% 0.046% 17 0.2924243921 0.9563690285 0.0000526874 0.0000642725 0.018% 0.007% 18 0.3073310520 0.9516834391 0.0016859423 0.0006269228 0.549% 0.066% 19 0.3251452218 0.9457453825 0.0004229326 0.0002268069 0.130% 0.024% 20 0.3435512762 0.9392157709 0.0015311329 0.0004768498 0.446% 0.051% 21 0.3581836921 0.9337334665 0.0001842574 0.0001530400 0.051% 0.016% 22 0.3763350045 0.9265666237 0.0017284111 0.0006172309 0.459% 0.067% 23 0.3907657879 0.9205736488 0.0000346594 0.0000687953 0.009% 0.007% 24 0.4050994316 0.9143567106 0.0016372115 0.0008112530 0.404% 0.089% 25 0.4228792420 0.9062708704 0.0002609802 0.0000369167 0.062% 0.004% 26 0.4368623186 0.8996138385 0.0015088282 0.0008197922 0.345% 0.091% 27 0.4543481744 0.8909104782 0.0003576747 0.0000960460 0.079% 0.011% 28 0.4682106568 0.8837038670 0.0012609060 0.0007562742 0.269% 0.086% 29 0.4853645826 0.8743997745 0.0005549624 0.0002199326 0.114% 0.025% 30 0.4989670003 0.8667096840 0.0010329997 0.0006842803 0.207% 0.079% 31 0.5157967696 0.8568006981 0.0007586947 0.0003666026 0.147% 0.043% 32 0.5291205039 0.8486372819 0.0007987604 0.0005891857 0.151% 0.069% 33 0.5446674419 0.8387437758 0.0000284069 0.0000732079 0.005% 0.009% 34 0.5577055299 0.8301314870 0.0014873735 0.0010939144 0.267% 0.132% 35 0.5738098078 0.8190824429 0.0002333714 0.0000696014 0.041% 0.008% 36 0.5865371490 0.8100172323 0.0012481033 0.0010002379 0.213% 0.123% 37 0.6022307543 0.7984183504 0.0004157312 0.0002171596 0.069% 0.027% 38 0.6146302652 0.7889127841 0.0010312101 0.0009020305 0.168% 0.114% 39 0.6299202599 0.7767587849 0.0005998689 0.0003871766 0.095% 0.050% 40 0.6418729918 0.7669112114 0.0009146178 0.0008667682 0.142% 0.113% 41 0.6567280845 0.7542293861 0.0006690555 0.0004801942 0.102% 0.064% 42 0.6684306187 0.7438778473 0.0006999876 0.0007330218 0.105% 0.099% 43 0.6828308344 0.7306817333 0.0008324743 0.0006719683 0.122% 0.092% 44 0.6941513412 0.7199358716 0.0005070292 0.0005960713 0.073% 0.083% 45 0.7062435465 0.7080775359 0.0008632347 0.0009707547 0.122% 0.137% 46 0.7199358716 0.6941513412 0.0005960713 0.0005070292 0.083% 0.073% 47 0.7306817333 0.6828308344 0.0006719683 0.0008324743 0.092% 0.122% 48 0.7438778473 0.6684306187 0.0007330218 0.0006999876 0.099% 0.105% 49 0.7542293861 0.6567280845 0.0004801942 0.0006690555 0.064% 0.102% 50 0.7669112114 0.6418729918 0.0008667682 0.0009146178 0.113% 0.142% 51 0.7767587849 0.6299202599 0.0003871766 0.0005998689 0.050% 0.095% 52 0.7889127841 0.6146302652 0.0009020305 0.0010312101 0.114% 0.168% 53 0.7984183504 0.6022307543 0.0002171596 0.0004157312 0.027% 0.069% 54 0.8100172323 0.5865371490 0.0010002379 0.0012481033 0.123% 0.213% 55 0.8190824429 0.5738098078 0.0000696014 0.0002333714 0.008% 0.041% 56 0.8301314870 0.5577055299 0.0010939144 0.0014873735 0.132% 0.267% 57 0.8387437758 0.5446674419 0.0000732079 0.0000284069 0.009% 0.005% 58 0.8486372819 0.5291205039 0.0005891857 0.0007987604 0.069% 0.151% 59 0.8568006981 0.5157967696 0.0003666026 0.0007586947 0.043% 0.147% 60 0.8667096840 0.4989670003 0.0006842803 0.0010329997 0.079% 0.207% 61 0.8743997745 0.4853645826 0.0002199326 0.0005549624 0.025% 0.114% 62 0.8837038670 0.4682106568 0.0007562742 0.0012609060 0.086% 0.269% 63 0.8909104782 0.4543481744 0.0000960460 0.0003576747 0.011% 0.079% 64 0.8996138385 0.4368623186 0.0008197922 0.0015088282 0.091% 0.345% 65 0.9062708704 0.4228792420 0.0000369167 0.0002609802 0.004% 0.062% 66 0.9143567106 0.4050994316 0.0008112530 0.0016372115 0.089% 0.404% 67 0.9205736488 0.3907657879 0.0000687953 0.0000346594 0.007% 0.009% 68 0.9265666237 0.3763350045 0.0006172309 0.0017284111 0.067% 0.459% 69 0.9337334665 0.3581836921 0.0001530400 0.0001842574 0.016% 0.051% 70 0.9392157709 0.3435512762 0.0004768498 0.0015311329 0.051% 0.446% 71 0.9457453825 0.3251452218 0.0002268069 0.0004229326 0.024% 0.130% 72 0.9516834391 0.3073310520 0.0006269228 0.0016859423 0.066% 0.549% 73 0.9563690285 0.2924243921 0.0000642725 0.0000526874 0.007% 0.018% 74 0.9608206145 0.2774481762 0.0004410814 0.0018108204 0.046% 0.653% 75 0.9660513338 0.2586475676 0.0001255075 0.0001714775 0.013% 0.066% 76 0.9699745364 0.2435223655 0.0003211899 0.0016004699 0.033% 0.657% 77 0.9745450275 0.2245344811 0.0001749627 0.0004165732 0.018% 0.186% 78 0.9779342089 0.2092807371 0.0002133919 0.0013690463 0.022% 0.654% 79 0.9818084619 0.1902784482 0.0001812785 0.0005305471 0.018% 0.279% 80 0.9846615389 0.1749154013 0.0001462141 0.0012672237 0.015% 0.724% 81 0.9878894877 0.1556537948 0.0002011471 0.0007806702 0.020% 0.502% 82 0.9902008452 0.1401999641 0.0000672235 0.0010268631 0.007% 0.732% 83 0.9927479474 0.1208522102 0.0002017957 0.0010171332 0.020% 0.842% 84 0.9945147694 0.1053286152 0.0000071260 0.0008001519 0.001% 0.760% 85 0.9963821278 0.0858859660 0.0001874297 0.0012697767 0.019% 1.478% 86 0.9975241564 0.0714093909 0.0000398938 0.0016529171 0.004% 2.315% 87 0.9987285075 0.0519144682 0.0000989728 0.0004214880 0.010% 0.812% 88 0.9994176447 0.0363058568 0.0000268177 0.0014063601 0.003% 3.874% 89 0.9999360752 0.0167806202 0.0000883801 0.0006717863 0.009% 4.003% 90 1.0000761814 0.0011726802 0.0000761814 0.0011726802 0.008% 100.000%
12. Složitější program v C: výpočty algoritmu CORDIC ve fixed point aritmetice
Zatímco byl původní program pro výpočet hodnot sinu a kosinu s využitím algoritmu CORDIC založen na výpočtech prováděných s numerickými hodnotami s plovoucí řádovou čárkou (floating point), ukážeme si v této kapitole stejný algoritmus, ovšem upravený tak, aby se výpočty prováděly s numerickými hodnotami s pevnou řádovou čárkou (fixed point), konkrétně s hodnotami, které mají před binární řádovou tečkou šestnáct bitů a za řádovou tečkou taktéž šestnáct bitů (lze ovlivnit konstantami A a B). Otestujeme tedy, jak jsou tyto v mnoha ohledech nízkoúrovňové operace založené na bitových operacích převedeny z jazyka C do jazyka Go (program navíc přímo tiskne tabulku s výsledky v HTML):
#include <stdlib.h> #include <stdio.h> #include <math.h> /* počet míst před a za binární řádovou tečkou */ #define A 16 #define B 16 /* Ludolfovo číslo */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* maximální počet iterací při běhu algoritmu */ #define MAX_ITER 16 /* "zesílení" při rotacích */ #define K_float 0.6073 /* převody mezi stupni a radiány */ #define rad2deg(rad) ((rad)*180.0/M_PI) #define deg2rad(deg) ((deg)/180.0*M_PI) /* datový typ, se kterým budeme pracovat */ typedef signed int fx; /* hlavičky použitých funkcí */ void fx_print(fx x); fx fp2fx(double x); double fx2fp(fx x); /* tabulka arkustangentu úhlů */ fx atans[MAX_ITER]; /* tabulka záporných celočíselných mocnin hodnoty 2 */ fx pows[MAX_ITER]; /* * Tisk numerické hodnoty uložené ve formátu pevné * řádové binární čárky (FX) */ void fx_print(fx x) { int i; int val = x; /* pomocná proměnná pro převod do dvojkové soustavy */ printf("bin: "); for (i = 0; i < A + B; i++) { /* převod na řetězec bitů (do dvojkové soustavy) */ putchar(!!(val & (1 << (A + B - 1))) + '0'); /* výpis hodnoty aktuálně nejvyššího bitu */ if (i == B - 1) putchar('.'); /* po řádové binární čárce vypsat značku */ val = val << 1; /* posun na další (méně významný) bit */ } printf(" hex: %08x fp: %+11.5f\n", x, fx2fp(x)); } /* * Převod z formátu plovoucí řádové binární čárky (FP) * do formátu pevné řádové binární čárky (FX) */ fx fp2fx(double x) { return (fx) (x * (2 << (B - 1))); } /* * Převod z celočíselného formátu (integer) * do formátu pevné řádové binární čárky (FX) */ fx int2fx(int x) { return (fx) (x << B); } /* * Převod z formátu pevné řádové binární čárky (FX) * do formátu plovoucí řádové binární čárky (FP) */ double fx2fp(fx x) { return (double) x / (2 << (B - 1)); } /* * Součet dvou hodnot uložených ve shodném formátu * pevné binární řádové čárky (FX) */ fx fx_add(fx x, fx y) { return x + y; } /* * Rozdíl dvou hodnot uložených ve shodném formátu * pevné binární řádové čárky (FX) */ fx fx_sub(fx x, fx y) { return x - y; } /* * Součin dvou hodnot uložených ve shodném formátu * pevné binární řádové čárky (FX) */ fx fx_mul(fx x, fx y) { fx result = (x >> (B / 2)) * (y >> (B / 2)); return result; } /* * Podíl dvou hodnot uložených ve shodném formátu * pevné binární řádové čárky (FX) */ fx fx_div(fx x, fx y) { fx result = x / (y >> (B / 2)); return result << (B / 2); } /* * Vytvoření tabulky pro výpočet goniometrických * funkcí pomocí algoritmu CORDIC */ void fx_create_tables(void) { int i; for (i = 0; i < MAX_ITER; i++) { double p = pow(2.0, -i); atans[i] = fp2fx(atan(p)); pows[i] = fp2fx(p); } } /* výpočet funkce tan() pro zadaný úhel delta */ /* (neoptimalizovaná verze) */ fx fx_tan_cordic(fx delta) { int i; /* nastavení počátečních podmínek */ fx x0 = fp2fx(1.0); fx y0 = fp2fx(0.0); fx xn; if (delta == 0) return 0; /* ošetření nulového úhlu */ for (i = 0; i < MAX_ITER; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, fx_mul(y0, pows[i])); y0 = fx_sub(y0, fx_mul(x0, pows[i])); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, fx_mul(y0, pows[i])); y0 = fx_add(y0, fx_mul(x0, pows[i])); delta = fx_sub(delta, atans[i]); } x0 = xn; /* printf("%i\t%+f\t%+f\t%+f\n", i, fx2fp(x0), fx2fp(y0), fx2fp(delta)); */ } if (x0 == 0) /* ošetření tangenty pravého úhlu */ if (y0 < 0) return 0; else return 0; else return fx_div(y0, x0); /* vrátit výsledek operace */ } /* výpočet funkce tan() pro zadaný úhel delta */ /* (optimalizovaná verze) */ fx fx_tan_cordic_optim(fx delta) { int i; /* nastavení počátečních podmínek */ fx x0 = int2fx(1); fx y0 = 0; fx xn; if (delta == 0) return 0; /* ošetření nulového úhlu */ for (i = 0; i < MAX_ITER; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, y0 >> i); /* místo násobení bitový posuv */ y0 = fx_sub(y0, x0 >> i); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, y0 >> i); y0 = fx_add(y0, x0 >> i); delta = fx_sub(delta, atans[i]); } x0 = xn; } if (x0 == 0) /* ošetření tangenty pravého úhlu */ if (y0 < 0) return 0; else return 0; else return fx_div(y0, x0); /* vrátit výsledek operace */ } /* výpočet funkce sin() pro zadaný úhel delta */ fx fx_sin_cordic_optim(fx delta) { int i; static fx K_fx = (fx) (K_float * (2 << (B - 1))); /* nastavení počátečních podmínek */ fx x0 = int2fx(1); fx y0 = 0; fx xn; for (i = 0; i < MAX_ITER; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, y0 >> i); /* místo násobení bitový posuv */ y0 = fx_sub(y0, x0 >> i); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, y0 >> i); y0 = fx_add(y0, x0 >> i); delta = fx_sub(delta, atans[i]); } x0 = xn; } return fx_mul(y0, K_fx); /* opravit "zesílení" výsledku */ } /* výpočet funkce cos() pro zadaný úhel delta */ fx fx_cos_cordic_optim(fx delta) { int i; static fx K_fx = (fx) (K_float * (2 << (B - 1))); /* nastavení počátečních podmínek */ fx x0 = int2fx(1); fx y0 = 0; fx xn; for (i = 0; i < MAX_ITER; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, y0 >> i); /* místo násobení bitový posuv */ y0 = fx_sub(y0, x0 >> i); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, y0 >> i); y0 = fx_add(y0, x0 >> i); delta = fx_sub(delta, atans[i]); } x0 = xn; } return fx_mul(x0, K_fx); /* opravit "zesílení" výsledku */ } /* výpočet funkce sin() pro zadaný úhel delta */ fx fx_sin_cordic_optim_iter(fx delta, int iter) { int i; static fx K_fx = (fx) (K_float * (2 << (B - 1))); /* nastavení počátečních podmínek */ fx x0 = int2fx(1); fx y0 = 0; fx xn; for (i = 0; i < iter; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, y0 >> i); /* místo násobení bitový posuv */ y0 = fx_sub(y0, x0 >> i); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, y0 >> i); y0 = fx_add(y0, x0 >> i); delta = fx_sub(delta, atans[i]); } x0 = xn; } return fx_mul(y0, K_fx); /* opravit "zesílení" výsledku */ } /* výpočet funkce cos() pro zadaný úhel delta */ fx fx_cos_cordic_optim_iter(fx delta, int iter) { int i; static fx K_fx = (fx) (K_float * (2 << (B - 1))); /* nastavení počátečních podmínek */ fx x0 = int2fx(1); fx y0 = 0; fx xn; for (i = 0; i < iter; i++) { /* iterační smyčka */ if (delta < 0) { /* úhel je záporný => rotace doleva */ xn = fx_add(x0, y0 >> i); /* místo násobení bitový posuv */ y0 = fx_sub(y0, x0 >> i); delta = fx_add(delta, atans[i]); } else { /* úhel je kladný => rotace doprava */ xn = fx_sub(x0, y0 >> i); y0 = fx_add(y0, x0 >> i); delta = fx_sub(delta, atans[i]); } x0 = xn; } return fx_mul(x0, K_fx); /* opravit "zesílení" výsledku */ } int main(void) { int i; fx cosfx; double delta; /* úhel, ze kterého se funkce počítá */ double value; /* vypočtené hodnoty */ double abs_err; /* absolutní chyby */ double rel_err; /* relativní chyby */ char *zvyr1, *zvyr2; /* ukazatele na konstantní řetězce pro */ /* generování HTML */ fx_create_tables(); puts("\n<h2>Výpočet funkce cos() optimalizovanou metodou CORDIC</h2>\n"); puts("<table>"); printf ("<tr><th>Úhel</th><th>cos FP</th><th>cos FX</th><th>Abs.chyba</th><th>Rel.chyba</th></tr>\n"); for (i = 0; i <= 90; i++) { /* výpočetní smyčka */ delta = deg2rad(i); /* převod úhlu na radiány */ cosfx = fx_cos_cordic_optim(fp2fx(delta)); /* aplikace algoritmu CORDIC */ value = fx2fp(cosfx); /* výpočet funkce cos */ abs_err = fabs(value - cos(delta)); /* výpočet absolutních chyb */ rel_err = cos(delta) <= 1e-10 ? 0 : 100.0 * abs_err / cos(delta); if (rel_err <= 1.0) { zvyr1 = "<strong>"; zvyr2 = "</strong>"; } else { zvyr1 = ""; zvyr2 = ""; } printf ("<tr><td>%02d</td><td>%5.3f</td><td>%5.3f%%</td><td>%5.3f</td><td>%s%5.3f%%%s</td></tr>\n", i, value, cos(delta), abs_err, zvyr1, rel_err, zvyr2); } puts("</table>"); return 0; } // finito
13. Automatická transpilace programu do jazyka Go
Po transpilaci zdrojového kódu z předchozí kapitoly získáme tento kód reprezentovaný v jazyce Go. Opět si povšimněte několika poznámek s varováním o interních problémech překladače:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ package main import "math" import "github.com/Konstantin8105/c4go/noarch" import "fmt" // fx - transpiled function from /root/fx.c:25 // počet míst před a za binární řádovou tečkou // Ludolfovo číslo // maximální počet iterací při běhu algoritmu // "zesílení" při rotacích // převody mezi stupni a radiány // datový typ, se kterým budeme pracovat /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */ /* AST Error : unknown node type: `value: Int 0` */ /* AST Error : unknown node type: `value: Int 1` */ /* AST Error : unknown node type: `value: Int 2` */ /* AST Error : unknown node type: `value: Int 3` */ /* AST Error : unknown node type: `value: Int 4` */type fx = int32 // atans - transpiled function from /root/fx.c:33 // hlavičky použitých funkcí // tabulka arkustangentu úhlů var atans []fx = make([]fx, 16) // pows - transpiled function from /root/fx.c:36 // tabulka záporných celočíselných mocnin hodnoty 2 var pows []fx = make([]fx, 16) // fx_print - transpiled function from /root/fx.c:42 func fx_print(x fx) { // // * Tisk numerické hodnoty uložené ve formátu pevné // * řádové binární čárky (FX) // var i int32 // pomocná proměnná pro převod do dvojkové soustavy var val int32 = int32((x)) fmt.Printf("bin: ") { // převod na řetězec bitů (do dvojkové soustavy) for i = 0; i < 16+16; i++ { // výpis hodnoty aktuálně nejvyššího bitu noarch.Putchar(noarch.BoolToInt(!noarch.Not(val&(1<<uint64(16+16-1)))) + int32('0')) if i == 16-1 { // po řádové binární čárce vypsat značku noarch.Putchar(int32('.')) } // posun na další (méně významný) bit val = val << uint64(1) } } noarch.Printf([]byte(" hex: %08x fp: %+11.5f\n\x00"), x, fx2fp(x)) } // fp2fx - transpiled function from /root/fx.c:61 func fp2fx(x float64) fx { // // * Převod z formátu plovoucí řádové binární čárky (FP) // * do formátu pevné řádové binární čárky (FX) // return fx(x * float64(2<<uint64(16-1))) } // int2fx - transpiled function from /root/fx.c:70 func int2fx(x int32) fx { // // * Převod z celočíselného formátu (integer) // * do formátu pevné řádové binární čárky (FX) // return fx(x << uint64(16)) } // fx2fp - transpiled function from /root/fx.c:79 func fx2fp(x fx) float64 { // // * Převod z formátu pevné řádové binární čárky (FX) // * do formátu plovoucí řádové binární čárky (FP) // return float64(int32((x))) / float64(2<<uint64(16-1)) } // fx_add - transpiled function from /root/fx.c:88 func fx_add(x fx, y fx) fx { // // * Součet dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // return x + y } // fx_sub - transpiled function from /root/fx.c:97 func fx_sub(x fx, y fx) fx { // // * Rozdíl dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // return x - y } // fx_mul - transpiled function from /root/fx.c:106 func fx_mul(x fx, y fx) fx { // // * Součin dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // var result fx = x >> uint64(16/2) * (y >> uint64(16/2)) return result } // fx_div - transpiled function from /root/fx.c:116 func fx_div(x fx, y fx) fx { // // * Podíl dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // var result fx = x / (y >> uint64(16/2)) return result << uint64(16/2) } // fx_create_tables - transpiled function from /root/fx.c:126 func fx_create_tables() { // // * Vytvoření tabulky pro výpočet goniometrických // * funkcí pomocí algoritmu CORDIC // var i int32 for i = 0; i < 16; i++ { var p float64 = math.Pow(2, float64(-i)) atans[i] = fp2fx(math.Atan(p)) pows[i] = fp2fx(p) } } // fx_tan_cordic - transpiled function from /root/fx.c:138 func fx_tan_cordic(delta fx) (c4goDefaultReturn fx) { // výpočet funkce tan() pro zadaný úhel delta // (neoptimalizovaná verze) var i int32 // nastavení počátečních podmínek var x0 fx = fp2fx(1) var y0 fx var xn fx if delta == fx((0)) { // ošetření nulového úhlu return fx((0)) } { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva xn = fx_add(x0, fx_mul(y0, pows[i])) y0 = fx_sub(y0, fx_mul(x0, pows[i])) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, fx_mul(y0, pows[i])) y0 = fx_add(y0, fx_mul(x0, pows[i])) delta = fx_sub(delta, atans[i]) } x0 = xn } } if x0 == fx((0)) { if y0 < fx((0)) { // printf("%i\t%+f\t%+f\t%+f\n", i, fx2fp(x0), fx2fp(y0), fx2fp(delta)); // ošetření tangenty pravého úhlu return fx((0)) } else { return fx((0)) } } else { // vrátit výsledek operace return fx_div(y0, x0) } return } // fx_tan_cordic_optim - transpiled function from /root/fx.c:171 func fx_tan_cordic_optim(delta fx) (c4goDefaultReturn fx) { // výpočet funkce tan() pro zadaný úhel delta // (optimalizovaná verze) var i int32 // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx if delta == fx((0)) { // ošetření nulového úhlu return fx((0)) } { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } if x0 == fx((0)) { if y0 < fx((0)) { // ošetření tangenty pravého úhlu return fx((0)) } else { return fx((0)) } } else { // vrátit výsledek operace return fx_div(y0, x0) } return } // fx_sin_cordic_optim - transpiled function from /root/fx.c:202 func fx_sin_cordic_optim(delta fx) fx { // výpočet funkce sin() pro zadaný úhel delta var i int32 var K_fx fx = fx(0.6073 * float64(2<<uint64(16-1))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(y0, K_fx) } // fx_cos_cordic_optim - transpiled function from /root/fx.c:226 func fx_cos_cordic_optim(delta fx) fx { // výpočet funkce cos() pro zadaný úhel delta var i int32 var K_fx fx = fx(0.6073 * float64(2<<uint64(16-1))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(x0, K_fx) } // fx_sin_cordic_optim_iter - transpiled function from /root/fx.c:250 func fx_sin_cordic_optim_iter(delta fx, iter int32) fx { // výpočet funkce sin() pro zadaný úhel delta var i int32 var K_fx fx = fx(0.6073 * float64(2<<uint64(16-1))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < iter; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(y0, K_fx) } // fx_cos_cordic_optim_iter - transpiled function from /root/fx.c:274 func fx_cos_cordic_optim_iter(delta fx, iter int32) fx { // výpočet funkce cos() pro zadaný úhel delta var i int32 var K_fx fx = fx(0.6073 * float64(2<<uint64(16-1))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < iter; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(x0, K_fx) } // main - transpiled function from /root/fx.c:297 func main() { defer noarch.AtexitRun() var i int32 var cosfx fx // úhel, ze kterého se funkce počítá var delta float64 // vypočtené hodnoty var value float64 // absolutní chyby var abs_err float64 // relativní chyby var rel_err float64 // ukazatele na konstantní řetězce pro var zvyr1 []byte var zvyr2 []byte // generování HTML fx_create_tables() noarch.Puts([]byte("\n<h2>Výpočet funkce cos() optimalizovanou metodou CORDIC</h2>\n\x00")) noarch.Puts([]byte("<table>\x00")) fmt.Printf("<tr><th>Úhel</th><th>cos FP</th><th>cos FX</th><th>Abs.chyba</th><th>Rel.chyba</th></tr>\n") { // výpočetní smyčka for i = 0; i <= 90; i++ { // převod úhlu na radiány delta = float64(i) / 180 * 3.141592653589793 // aplikace algoritmu CORDIC cosfx = fx_cos_cordic_optim(fp2fx(delta)) // výpočet funkce cos value = fx2fp(cosfx) // výpočet absolutních chyb abs_err = math.Abs(value - math.Cos(delta)) if math.Cos(delta) <= 1e-10 { rel_err = 0 } else { rel_err = 100 * abs_err / math.Cos(delta) } if rel_err <= 1 { zvyr1 = []byte("<strong>\x00") zvyr2 = []byte("</strong>\x00") } else { zvyr1 = []byte("\x00") zvyr2 = []byte("\x00") } noarch.Printf([]byte("<tr><td>%02d</td><td>%5.3f</td><td>%5.3f%%</td><td>%5.3f</td><td>%s%5.3f%%%s</td></tr>\n\x00"), i, value, math.Cos(delta), abs_err, zvyr1, rel_err, zvyr2) } } noarch.Puts([]byte("</table>\x00")) return } // finito
14. Ruční oprava některých chyb vzniklých při transpilaci
Překlad transpilovaného zdrojového kódu v jeho původní podobně není možný (chyba c4go):
$ go build fx_transpiled.go # command-line-arguments ./fx_transpiled.go:97:52: (1 << uint64(16 + 16 - 1)) (untyped int constant 2147483648) overflows int32 ./fx_transpiled.go:282:19: cannot convert 0.6073 * float64(2 << uint64(16 - 1)) (constant 39800 of type float64) to type int32 ./fx_transpiled.go:313:19: cannot convert 0.6073 * float64(2 << uint64(16 - 1)) (constant 39800 of type float64) to type int32 ./fx_transpiled.go:344:19: cannot convert 0.6073 * float64(2 << uint64(16 - 1)) (constant 39800 of type float64) to type int32 ./fx_transpiled.go:375:19: cannot convert 0.6073 * float64(2 << uint64(16 - 1)) (constant 39800 of type float64) to type int32
Z tohoto důvodu je nutné provést několik úprav s tím, že následující zdrojový kód je již bez problémů jak přeložitelný, tak i spustitelný a navíc poskytne stejné výsledky, jako originální céčkový kód:
var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1))))
Výsledek:
package main import "math" import "github.com/Konstantin8105/c4go/noarch" import "fmt" type fx int32 // atans - transpiled function from /root/fx.c:33 // hlavičky použitých funkcí // tabulka arkustangentu úhlů var atans []fx = make([]fx, 16) // pows - transpiled function from /root/fx.c:36 // tabulka záporných celočíselných mocnin hodnoty 2 var pows []fx = make([]fx, 16) // fx_print - transpiled function from /root/fx.c:42 func fx_print(x fx) { // // * Tisk numerické hodnoty uložené ve formátu pevné // * řádové binární čárky (FX) // var i int32 // pomocná proměnná pro převod do dvojkové soustavy var val uint32 = uint32((x)) fmt.Printf("bin: ") { // převod na řetězec bitů (do dvojkové soustavy) for i = 0; i < 16+16; i++ { // výpis hodnoty aktuálně nejvyššího bitu bit := val & (1 << (16 + 16 - 1)) noarch.Putchar(noarch.BoolToInt(!noarch.Not(bit)) + int32('0')) if i == 16-1 { // po řádové binární čárce vypsat značku noarch.Putchar(int32('.')) } // posun na další (méně významný) bit val = val << uint64(1) } } noarch.Printf([]byte(" hex: %08x fp: %+11.5f\n\x00"), x, fx2fp(x)) } // fp2fx - transpiled function from /root/fx.c:61 func fp2fx(x float64) fx { // // * Převod z formátu plovoucí řádové binární čárky (FP) // * do formátu pevné řádové binární čárky (FX) // return fx(x * float64(2<<uint64(16-1))) } // int2fx - transpiled function from /root/fx.c:70 func int2fx(x int32) fx { // // * Převod z celočíselného formátu (integer) // * do formátu pevné řádové binární čárky (FX) // return fx(x << uint64(16)) } // fx2fp - transpiled function from /root/fx.c:79 func fx2fp(x fx) float64 { // // * Převod z formátu pevné řádové binární čárky (FX) // * do formátu plovoucí řádové binární čárky (FP) // return float64(int32((x))) / float64(2<<uint64(16-1)) } // fx_add - transpiled function from /root/fx.c:88 func fx_add(x fx, y fx) fx { // // * Součet dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // return x + y } // fx_sub - transpiled function from /root/fx.c:97 func fx_sub(x fx, y fx) fx { // // * Rozdíl dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // return x - y } // fx_mul - transpiled function from /root/fx.c:106 func fx_mul(x fx, y fx) fx { // // * Součin dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // var result fx = x >> uint64(16/2) * (y >> uint64(16/2)) return result } // fx_div - transpiled function from /root/fx.c:116 func fx_div(x fx, y fx) fx { // // * Podíl dvou hodnot uložených ve shodném formátu // * pevné binární řádové čárky (FX) // var result fx = x / (y >> uint64(16/2)) return result << uint64(16/2) } // fx_create_tables - transpiled function from /root/fx.c:126 func fx_create_tables() { // // * Vytvoření tabulky pro výpočet goniometrických // * funkcí pomocí algoritmu CORDIC // var i int32 for i = 0; i < 16; i++ { var p float64 = math.Pow(2, float64(-i)) atans[i] = fp2fx(math.Atan(p)) pows[i] = fp2fx(p) } } // fx_tan_cordic - transpiled function from /root/fx.c:138 func fx_tan_cordic(delta fx) (c4goDefaultReturn fx) { // výpočet funkce tan() pro zadaný úhel delta // (neoptimalizovaná verze) var i int32 // nastavení počátečních podmínek var x0 fx = fp2fx(1) var y0 fx var xn fx if delta == fx((0)) { // ošetření nulového úhlu return fx((0)) } { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva xn = fx_add(x0, fx_mul(y0, pows[i])) y0 = fx_sub(y0, fx_mul(x0, pows[i])) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, fx_mul(y0, pows[i])) y0 = fx_add(y0, fx_mul(x0, pows[i])) delta = fx_sub(delta, atans[i]) } x0 = xn } } if x0 == fx((0)) { if y0 < fx((0)) { // printf("%i\t%+f\t%+f\t%+f\n", i, fx2fp(x0), fx2fp(y0), fx2fp(delta)); // ošetření tangenty pravého úhlu return fx((0)) } else { return fx((0)) } } else { // vrátit výsledek operace return fx_div(y0, x0) } return } // fx_tan_cordic_optim - transpiled function from /root/fx.c:171 func fx_tan_cordic_optim(delta fx) (c4goDefaultReturn fx) { // výpočet funkce tan() pro zadaný úhel delta // (optimalizovaná verze) var i int32 // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx if delta == fx((0)) { // ošetření nulového úhlu return fx((0)) } { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } if x0 == fx((0)) { if y0 < fx((0)) { // ošetření tangenty pravého úhlu return fx((0)) } else { return fx((0)) } } else { // vrátit výsledek operace return fx_div(y0, x0) } return } // fx_sin_cordic_optim - transpiled function from /root/fx.c:202 func fx_sin_cordic_optim(delta fx) fx { // výpočet funkce sin() pro zadaný úhel delta var i int32 var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(y0, K_fx) } // fx_cos_cordic_optim - transpiled function from /root/fx.c:226 func fx_cos_cordic_optim(delta fx) fx { // výpočet funkce cos() pro zadaný úhel delta var i int32 var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < 16; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(x0, K_fx) } // fx_sin_cordic_optim_iter - transpiled function from /root/fx.c:250 func fx_sin_cordic_optim_iter(delta fx, iter int32) fx { // výpočet funkce sin() pro zadaný úhel delta var i int32 var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < iter; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(y0, K_fx) } // fx_cos_cordic_optim_iter - transpiled function from /root/fx.c:274 func fx_cos_cordic_optim_iter(delta fx, iter int32) fx { // výpočet funkce cos() pro zadaný úhel delta var i int32 var K_fx fx = fx(math.Round(0.6073 * float64(2<<uint64(16-1)))) // nastavení počátečních podmínek var x0 fx = int2fx(1) var y0 fx = fx((0)) var xn fx { // iterační smyčka for i = 0; i < iter; i++ { if delta < fx((0)) { // úhel je záporný => rotace doleva // místo násobení bitový posuv xn = fx_add(x0, y0>>uint64(i)) y0 = fx_sub(y0, x0>>uint64(i)) delta = fx_add(delta, atans[i]) } else { // úhel je kladný => rotace doprava xn = fx_sub(x0, y0>>uint64(i)) y0 = fx_add(y0, x0>>uint64(i)) delta = fx_sub(delta, atans[i]) } x0 = xn } } // opravit "zesílení" výsledku return fx_mul(x0, K_fx) } // main - transpiled function from /root/fx.c:297 func main() { defer noarch.AtexitRun() var i int32 var cosfx fx // úhel, ze kterého se funkce počítá var delta float64 // vypočtené hodnoty var value float64 // absolutní chyby var abs_err float64 // relativní chyby var rel_err float64 // ukazatele na konstantní řetězce pro var zvyr1 []byte var zvyr2 []byte // generování HTML fx_create_tables() noarch.Puts([]byte("\n<h2>Výpočet funkce cos() optimalizovanou metodou CORDIC</h2>\n\x00")) noarch.Puts([]byte("<table>\x00")) fmt.Printf("<tr><th>Úhel</th><th>cos FP</th><th>cos FX</th><th>Abs.chyba</th><th>Rel.chyba</th></tr>\n") { // výpočetní smyčka for i = 0; i <= 90; i++ { // převod úhlu na radiány delta = float64(i) / 180 * 3.141592653589793 // aplikace algoritmu CORDIC cosfx = fx_cos_cordic_optim(fp2fx(delta)) // výpočet funkce cos value = fx2fp(cosfx) // výpočet absolutních chyb abs_err = math.Abs(value - math.Cos(delta)) if math.Cos(delta) <= 1e-10 { rel_err = 0 } else { rel_err = 100 * abs_err / math.Cos(delta) } if rel_err <= 1 { zvyr1 = []byte("<strong>\x00") zvyr2 = []byte("</strong>\x00") } else { zvyr1 = []byte("\x00") zvyr2 = []byte("\x00") } noarch.Printf([]byte("<tr><td>%02d</td><td>%5.3f</td><td>%5.3f%%</td><td>%5.3f</td><td>%s%5.3f%%%s</td></tr>\n\x00"), i, value, math.Cos(delta), abs_err, zvyr1, rel_err, zvyr2) } } noarch.Puts([]byte("</table>\x00")) return } // finito
15. Porovnání výsledků výpočtů
Výsledky vypočtené originálním programem napsaným v céčku jsou naprosto shodné s výsledky, které lze získat transpilovaným programem reprezentovaným v jazyce Go. Zde je uvedena pouze krátká ukázka vygenerované tabulky s výsledky:
Úhel | cos FP | cos FX | Abs.chyba | Rel.chyba |
---|---|---|---|---|
00 | 0.996 | 1.000% | 0.004 | 0.429% |
01 | 0.996 | 1.000% | 0.004 | 0.414% |
02 | 0.996 | 0.999% | 0.004 | 0.368% |
03 | 0.993 | 0.999% | 0.005 | 0.529% |
04 | 0.993 | 0.998% | 0.004 | 0.423% |
05 | 0.991 | 0.996% | 0.005 | 0.523% |
06 | 0.991 | 0.995% | 0.004 | 0.356% |
07 | 0.989 | 0.993% | 0.004 | 0.396% |
08 | 0.986 | 0.990% | 0.004 | 0.406% |
09 | 0.984 | 0.988% | 0.004 | 0.385% |
10 | 0.982 | 0.985% | 0.003 | 0.334% |
… | … | … | … | … |
… | … | … | … | … |
… | … | … | … | … |
76 | 0.239 | 0.242% | 0.003 | 1.259% |
77 | 0.222 | 0.225% | 0.003 | 1.169% |
78 | 0.206 | 0.208% | 0.002 | 1.033% |
79 | 0.189 | 0.191% | 0.002 | 0.839% |
80 | 0.173 | 0.174% | 0.001 | 0.573% |
81 | 0.154 | 0.156% | 0.003 | 1.727% |
82 | 0.137 | 0.139% | 0.002 | 1.435% |
83 | 0.121 | 0.122% | 0.001 | 1.025% |
84 | 0.104 | 0.105% | 0.000 | 0.443% |
85 | 0.085 | 0.087% | 0.002 | 2.308% |
86 | 0.069 | 0.070% | 0.001 | 1.675% |
87 | 0.052 | 0.052% | 0.000 | 0.580% |
88 | 0.033 | 0.035% | 0.002 | 5.123% |
89 | 0.017 | 0.017% | 0.001 | 5.138% |
90 | –0.002 | 0.000% | 0.002 | 0.000% |
16. Program psaný v C, který vytváří a manipuluje se stromovou datovou strukturou
Poslední příklad, který si dnes ukážeme, je opět psaný v jazyku C. Najdeme v něm triviální implementaci konstrukce binárního stromu pro uložení řetězců společně s funkcí pro průchod (traverzaci) tímto stromem. V tomto příkladu se tedy zaměřujeme na operace s ukazateli, využití struktur obsahujících ukazatele, dynamickou alokaci paměti atd. (tedy operace, které se v Go většinou provádí odlišně, než je tomu v jazyku C):
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct Node { struct Node *left; struct Node *right; char *value; } Node; void insert_new_node(Node **root, char *value) { int cmp; if (*root == NULL) { *root = (Node *)malloc(sizeof(Node)); (*root)->value = (char*)calloc(strlen(value), sizeof(char)); strcpy((*root)->value, value); (*root)->left = NULL; (*root)->right = NULL; return; } cmp = strcmp(value, (*root)->value); if (cmp < 0) { insert_new_node(&(*root)->left, value); } else { insert_new_node(&(*root)->right, value); } } void traverse_tree(Node *root, void (*callback_function)(char *)) { if (root == NULL) { return; } traverse_tree(root->left, callback_function); callback_function(root->value); traverse_tree(root->right, callback_function); } void callback_function(char *value) { printf("%s\n", value); } int main(void) { static Node *root = NULL; insert_new_node(&root, "xxx"); insert_new_node(&root, "aaa"); insert_new_node(&root, "bbb"); insert_new_node(&root, "ccc"); insert_new_node(&root, "yyy"); insert_new_node(&root, "yyy"); traverse_tree(root, callback_function); return 0; }
17. Výsledek transpilace programu do jazyka Go
Program z předchozí kapitoly se transpřeloží do tohoto zdrojového kódu reprezentovaného v jazyce Go:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // package main import "unsafe" import "github.com/Konstantin8105/c4go/noarch" // Node - transpiled function from /root/tree.c:5 type Node struct { left []Node right []Node value []byte } // insert_new_node - transpiled function from /root/tree.c:12 func insert_new_node(root [][]Node, value []byte) { var cmp int32 if len(root[0]) == 0 { root[0] = make([]Node, 1) (root[0])[0].value = make([]byte, noarch.Strlen(value)) noarch.Strcpy((root[0])[0].value, value) (root[0])[0].left = nil (root[0])[0].right = nil return } cmp = noarch.Strcmp(value, (root[0])[0].value) if cmp < 0 { insert_new_node((*[1000000][]Node)(unsafe.Pointer(&(root[0])[0].left))[:], value) } else { insert_new_node((*[1000000][]Node)(unsafe.Pointer(&(root[0])[0].right))[:], value) } } // traverse_tree - transpiled function from /root/tree.c:36 func traverse_tree(root []Node, callback_function func([]byte)) { if len(root) == 0 { return } traverse_tree(root[0].left, callback_function) callback_function(root[0].value) traverse_tree(root[0].right, callback_function) } // callback_function - transpiled function from /root/tree.c:47 func callback_function(value []byte) { noarch.Printf([]byte("%s\n\x00"), value) } // main - transpiled function from /root/tree.c:52 func main() { defer noarch.AtexitRun() var root []Node insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("xxx\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("aaa\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("bbb\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("ccc\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("yyy\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("yyy\x00")) traverse_tree(root, callback_function) return }
Tento program ovšem po svém spuštění zhavaruje:
panic: runtime error: index out of range [3] with length 3 goroutine 1 [running]: github.com/Konstantin8105/c4go/noarch.Strcpy(...) /home/ptisnovs/go/pkg/mod/github.com/!konstantin8105/c4go@v0.0.0-20211115111653-1c67b1543446/noarch/string.go:30 main.insert_new_node({0xc0000aff50, 0xc000048738?, 0x4589fb?}, {0xc0000aff44, 0x4, 0xc0000021a0?}) /home/ptisnovs/src/go-root/article_97/tree.go:26 +0x28a main.main() /home/ptisnovs/src/go-root/article_97/tree.go:58 +0x70 exit status 2
18. Oprava předchozího programu
Oprava spočívá v tom, že se upraví velikost bloku pro kopírovaný řetězec – viz též zvýrazněný kód. Po této úpravě bude manipulace se stromem probíhat korektně:
// // Package - transpiled by c4go // // If you have found any issues, please raise an issue at: // https://github.com/Konstantin8105/c4go/ // package main import "unsafe" import "github.com/Konstantin8105/c4go/noarch" // Node - transpiled function from /root/tree.c:5 type Node struct { left []Node right []Node value []byte } // insert_new_node - transpiled function from /root/tree.c:12 func insert_new_node(root [][]Node, value []byte) { var cmp int32 if len(root[0]) == 0 { root[0] = make([]Node, 1) (root[0])[0].value = make([]byte, 1+noarch.Strlen(value)) noarch.Strcpy((root[0])[0].value, value) (root[0])[0].left = nil (root[0])[0].right = nil return } cmp = noarch.Strcmp(value, (root[0])[0].value) if cmp < 0 { insert_new_node((*[1000000][]Node)(unsafe.Pointer(&(root[0])[0].left))[:], value) } else { insert_new_node((*[1000000][]Node)(unsafe.Pointer(&(root[0])[0].right))[:], value) } } // traverse_tree - transpiled function from /root/tree.c:36 func traverse_tree(root []Node, callback_function func([]byte)) { if len(root) == 0 { return } traverse_tree(root[0].left, callback_function) callback_function(root[0].value) traverse_tree(root[0].right, callback_function) } // callback_function - transpiled function from /root/tree.c:47 func callback_function(value []byte) { noarch.Printf([]byte("%s\n\x00"), value) } // main - transpiled function from /root/tree.c:52 func main() { defer noarch.AtexitRun() var root []Node insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("xxx\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("aaa\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("bbb\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("ccc\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("yyy\x00")) insert_new_node((*[1000000][]Node)(unsafe.Pointer(&root))[:], []byte("yyy\x00")) traverse_tree(root, callback_function) return }
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně stovku kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- c4go na GitHubu
https://github.com/Konstantin8105/c4go - Source-to-source compiler (Wikipedia)
https://en.wikipedia.org/wiki/Source-to-source_compiler - Clang: a C language family frontend for LLVM
https://clang.llvm.org/ - The LLVM Compiler Infrastructure
https://llvm.org/ - Abstract syntax tree
https://en.wikipedia.org/wiki/Abstract_syntax_tree - List of transpilers, sorted by target language
https://github.com/jarble/list-of-transpilers - Awesome WebAssembly Languages Awesome
https://github.com/appcypher/awesome-wasm-langs - Compilers targeting C
https://github.com/dbohdan/compilers-targeting-c - History of compiler construction
https://en.wikipedia.org/wiki/History_of_compiler_construction - Golang AST Package
https://golangdocs.com/golang-ast-package - Rune literals
https://go.dev/ref/spec#Rune_literals - Výpočet goniometrických funkcí algoritmem CORDIC
https://www.root.cz/clanky/vypocet-goniometrickych-funkci-algoritmem-cordic/ - Metoda CORDIC a výpočet funkcí tan, atan a length
https://www.root.cz/clanky/metoda-cordic-a-vypocet-funkci-tan-atan-a-length/ - Algoritmus CORDIC v FX formátu a goniometrické funkce
https://www.root.cz/clanky/algoritmus-cordic-v-fx-formatu-a-goniometricke-funkce/ - Algoritmus CORDIC s hodnotami uloženými ve formátu FX
https://www.root.cz/clanky/algoritmus-cordic-s-hodnotami-ulozenymi-ve-formatu-fx/ - unsafe.Pointer
https://pkg.go.dev/unsafe#Pointer