Hlavní navigace

Číselné hodnoty s neomezeným rozsahem a přesností v programovacím jazyku Go: typ Decimal

9. 5. 2023
Doba čtení: 28 minut

Sdílet

 Autor: Go lang
Navážeme na práci s numerickými hodnotami s neomezeným rozsahem a budeme se zabývat knihovnou shopspring/decimal, která programátorům nabízí hodnoty s desítkovým exponentem v rozsahu –2^31 až 2^31–1 a libovolně rozsáhlou mantisou.

Obsah

1. Číselné hodnoty s neomezeným rozsahem a přesností v programovacím jazyku Go: typ Decimal

2. Interní reprezentace numerických hodnot typu Decimal

3. Instalace balíčku shopspring/decimal

4. Nulová hodnota typu Decimal

5. Konstrukce hodnot typu Decimal s využitím inicializační celočíselné hodnoty

6. Konstrukce hodnot typu Decimal s využitím inicializační hodnoty s plovoucí řádovou čárkou

7. Explicitní specifikace hodnoty exponentu při konstrukci hodnot typu Decimal

8. Převod obsahu řetězce na numerickou hodnotu typu Decimal

9. Automatická kontrola, zda naparsování hodnoty z řetězce bylo úspěšné

10. Naparsování hodnoty z řetězce se specifikací nadbytečných znaků v řetězci

11. Základní aritmetické operace s hodnotami typu Decimal

12. Podíl se specifikací způsobu zaokrouhlení výsledku

13. Výpočet faktoriálu s hodnotami typu Decimal

14. Agregační funkce s hodnotami typu Decimal

15. Interní struktura hodnot typu Decimal

16. Výpočet hodnoty π s typem Decimal

17. Rychlost výpočtů s hodnotami typu Decimal

18. Výsledky benchmarků

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Číselné hodnoty s neomezeným rozsahem a přesností v programovacím jazyku Go: typ Decimal

V seriálu o programovacím jazyce Go jsme se již zabývali způsoby, jakými je možné v tomto programovacím jazyce pracovat s hodnotami, které mají neomezený rozsah a/nebo i přesnost [1] [2]. Připomeňme si, že se pro tento účel používají datové struktury definované ve standardním balíčku math/big; konkrétně se jedná o celá čísla s neomezeným rozsahem (big.Int), zlomky, jejichž čitatele i jmenovatele mají neomezený rozsah (big.Rat) a konečně hodnoty s plovoucí řádovou čárkou (big.Float). Existují ovšem situace, které vyžadují použití odlišných způsobů reprezentace rozsáhlých numerických hodnot. A jedna z možných reprezentací je nabízena v balíčku shopspring/decimal. Jak již název tohoto balíčku naznačuje, nejedná se o součást standardní knihovny, takže je nutná jeho (triviální) instalace.

Poznámka: pro Go existuje hned několik balíčků s podobným jménem; k některým z nich se ještě vrátíme.

2. Interní reprezentace numerických hodnot typu Decimal

Hodnoty typu Decimal jsou interně reprezentovány velmi jednoduchou datovou strukturou, která obsahuje pouze dva prvky. Prvním prvkem je hodnota typu big.Int, což je, jak již dobře víme, celočíselná hodnota s neomezeným rozsahem. Druhým prvkem je desítkový exponent reprezentovaný 32bitovou celočíselnou hodnotou int32. Důvod, proč je exponent takto „omezený“ pouze na 32 bitů, je uveden v poznámce:

type Decimal struct {
        value *big.Int
 
        // NOTE(vadim): this must be an int32, because we cast it to float64 during
        // calculations. If exp is 64 bit, we might lose precision.
        // If we cared about being able to represent every possible decimal, we
        // could make exp a *big.Int but it would hurt performance and numbers
        // like that are unrealistic.
        exp int32
}
Poznámka: v praxi však k žádnému omezení nedochází, protože rozsah desítkových (nikoli dvojkových!) exponentů od –2 31 do 231–1 je více než dostatečný pro všechny myslitelné výpočty.

3. Instalace balíčku shopspring/decimal

Samotná instalace balíčku shopspring/decimal je při použití některé novější verze základních nástrojů programovacího jazyka Go jednoduchá. Nejprve si vytvořte prázdný projekt příkazem:

$ go mod init decimal-1

Dále je nutné upravit soubor go.mod, který vznikne předchozím příkazem, do podoby:

module decimal-1
 
go 1.20
 
require github.com/shopspring/decimal v1.3.1

Nyní je již možné ve zdrojových souborech projektu provést import balíčku shopspring/decimal a začít používat zde definovaný datový typ Decimal:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
...
...
...

4. Nulová hodnota typu Decimal

Již ve standardním balíčku math.big je zajištěno, že explicitně neinicializovaná struktura daného typu big.Int, big.Rat nebo big.Float je považována za nulovou hodnotu, což může zjednodušovat zápis programů. Ostatně si to můžeme velmi snadno ověřit tímto triviálním příkladem, v němž pouze definujeme proměnné daného typu, ovšem bez jejich explicitní inicializace:

package main
 
import (
        "fmt"
        "math/big"
)
 
func main() {
        var x big.Int
        var y big.Rat
        var z big.Float
 
        fmt.Println("big.Int  ", x.Text(10))
        fmt.Println("big.Rat  ", y.String())
        fmt.Println("big.Float", z.Text('f', 10))
}

S těmito výsledky:

big.Int   0
big.Rat   0/1
big.Float 0.0000000000

Podobně je tomu i u datového typu Decimal, jehož neinicializované proměnné jsou automaticky považovány za nulové hodnoty:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        var d decimal.Decimal
        fmt.Println(d)
}

Po překladu a spuštění tohoto demonstračního příkladu se skutečně vypíše nula:

0
Poznámka: zde se počítá s tím, že v jazyce Go je explicitně specifikováno, že všechny hodnoty (i hodnoty prvků struktur) jsou inicializovány na nulu.

5. Konstrukce hodnot typu Decimal s využitím inicializační celočíselné hodnoty

Hodnoty typu Decimal je pochopitelně možné vytvářet a současně i inicializovat (na hodnotu odlišnou od nuly). Pro inicializaci lze použít i celočíselné hodnoty, konkrétně hodnoty typu int32 či int (což je v současnosti na většině architektur vlastně int64). Podívejme se nejdříve na ukázku použití konstruktoru, který akceptuje hodnoty typu int32:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.NewFromInt32(0)
        fmt.Println(d0)
 
        d1 := decimal.NewFromInt32(1234)
        fmt.Println(d1)
 
        d2 := decimal.NewFromInt32(12345)
        fmt.Println(d2)
 
        d3 := decimal.NewFromInt32(123456)
        fmt.Println(d3)
 
        d4 := decimal.NewFromInt32(1234567)
        fmt.Println(d4)
 
        d5 := decimal.NewFromInt32(12345678)
        fmt.Println(d5)
}

Výsledky vypsané po překladu a spuštění tohoto demonstračního příkladu pravděpodobně nebudou nijak překvapivé:

0
1234
12345
123456
1234567
12345678

Prakticky stejným způsobem, pouze s využitím odlišného konstruktoru, můžeme pro inicializaci datového typu Decimal použít i hodnoty typu int (tedy většinou int64):

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.NewFromInt(0)
        fmt.Println(d0)
 
        d1 := decimal.NewFromInt(123456)
        fmt.Println(d1)
 
        d2 := decimal.NewFromInt(123456789)
        fmt.Println(d2)
 
        d3 := decimal.NewFromInt(12345678900000)
        fmt.Println(d3)
 
        d4 := decimal.NewFromInt(1234567890000000000)
        fmt.Println(d4)
}

S výsledky:

0
123456
123456789
12345678900000
1234567890000000000
Poznámka: v těchto případech je převod pochopitelně proveden bez ztráty přesnosti a/nebo rozsahu.

6. Konstrukce hodnot typu Decimal s využitím inicializační hodnoty s plovoucí řádovou čárkou

K inicializaci hodnot typu Decimal můžeme pochopitelně použít i hodnotu s plovoucí řádovou čárkou, což v programovacím jazyku Go znamená typy float32 a float64. Na tomto místě je ale dobré si uvědomit, že typ Decimal nedokáže vyjádřit kladné a záporné nekonečno, takže při pokusu o převod těchto hodnot dojde k pádu (panic) celého programu (ovšem víme již, že tento pád lze zachytit).

Nejdříve si ukažme inicializaci s využitím vstupní hodnoty typu float32:

package main
 
import (
        "fmt"
        "math"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.NewFromFloat32(0)
        fmt.Println(d0)
 
        d1 := decimal.NewFromFloat32(1e10)
        fmt.Println(d1)
 
        d2 := decimal.NewFromFloat32(1e-10)
        fmt.Println(d2)
 
        // maximální podporovaný exponent
        d3 := decimal.NewFromFloat32(1.2e38)
        fmt.Println(d3)
 
        // minimální podporovaný exponent
        d4 := decimal.NewFromFloat32(1.2e-38)
        fmt.Println(d4)
 
        // de facto nula z pohledu float32
        d5 := decimal.NewFromFloat32(1.1e-100)
        fmt.Println(d5)
 
        // kladné nekonečno
        d6 := decimal.NewFromFloat32(float32(math.Inf(1)))
        fmt.Println(d6)
 
        // záporné nekonečno
        d7 := decimal.NewFromFloat32(float32(math.Inf(-1)))
        fmt.Println(d7)
}

Prvních pět hodnot se převede bez problémů:

0
10000000000
0.0000000001
120000000000000000000000000000000000000
0.000000000000000000000000000000000000012

Šestá hodnota je pro float32 tak malá, že se jedná o nulu:

0

A kladné či záporné nekonečno převést nelze – právě zde dojde k pádu aplikace:

panic: Cannot create a Decimal from +Inf
 
goroutine 1 [running]:
github.com/shopspring/decimal.newFromFloat(0xc00010a270?, 0x0?, 0x0?)
        /home/ptisnovs/go/pkg/mod/github.com/shopspring/decimal@v1.3.1/decimal.go:286 +0x3d4
github.com/shopspring/decimal.NewFromFloat32(0x4d49f8?)
        /home/ptisnovs/go/pkg/mod/github.com/shopspring/decimal@v1.3.1/decimal.go:281 +0x65
main.main()
        /home/ptisnovs/src/go-root/article_AA/04_construct_from_float_32/construct_from_float32.go:29 +0x2c5
exit status 2

Podobně můžeme postupovat při použití numerických hodnot reprezentovaných typem float64:

package main
 
import (
        "fmt"
        "math"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.NewFromFloat(0)
        fmt.Println(d0)
 
        d1 := decimal.NewFromFloat(1e10)
        fmt.Println(d1)
 
        d2 := decimal.NewFromFloat(1e-10)
        fmt.Println(d2)
 
        // maximální exponent
        d3 := decimal.NewFromFloat(1.2e308)
        fmt.Println(d3)
 
        // minimální exponent
        d4 := decimal.NewFromFloat(1.2e-308)
        fmt.Println(d4)
 
        // tato hodnota je již de facto nulová
        d5 := decimal.NewFromFloat(1.1e-1000)
        fmt.Println(d5)
 
        // kladné nekonečno
        d6 := decimal.NewFromFloat(math.Inf(1))
        fmt.Println(d6)
 
        // záporné nekonečno
        d7 := decimal.NewFromFloat(math.Inf(-1))
        fmt.Println(d7)
}

Podívejme se nyní na zobrazené výsledky, a to včetně pádu aplikace při pokusu o převedení nekonečné hodnoty na typ Decimal:

0
10000000000
0.0000000001
120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012
0
panic: Cannot create a Decimal from +Inf
 
goroutine 1 [running]:
github.com/shopspring/decimal.newFromFloat(0xc00010a270?, 0x0?, 0x0?)
        /home/ptisnovs/go/pkg/mod/github.com/shopspring/decimal@v1.3.1/decimal.go:286 +0x3d4
github.com/shopspring/decimal.NewFromFloat(0x4d49f8?)
        /home/ptisnovs/go/pkg/mod/github.com/shopspring/decimal@v1.3.1/decimal.go:262 +0x56
main.main()
        /home/ptisnovs/src/go-root/article_AA/05_construct_from_float/construct_from_float.go:29 +0x2c5
exit status 2

7. Explicitní specifikace hodnoty exponentu při konstrukci hodnot typu Decimal

Ve druhé kapitole jsme si ukázali, jakým způsobem jsou uloženy hodnoty typu Decimal. Víme tedy, že vlastní celočíselná hodnota je oddělena od exponentu. Zajímavé je, že při konstrukci hodnoty Decimal je možné hodnotu desítkového exponentu explicitně nastavit, konkrétně použitím konstruktoru NewFromFloatWithExponent. Podívejme se nyní na důsledky:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.NewFromFloatWithExponent(123.456, 0)
        fmt.Println(d0)
 
        d1 := decimal.NewFromFloatWithExponent(123.456, 1)
        fmt.Println(d1)
 
        d2 := decimal.NewFromFloatWithExponent(123.456, 2)
        fmt.Println(d2)
 
        d3 := decimal.NewFromFloatWithExponent(123.456, 3)
        fmt.Println(d3)
 
        d4 := decimal.NewFromFloatWithExponent(123.456, -1)
        fmt.Println(d4)
 
        d5 := decimal.NewFromFloatWithExponent(123.456, -2)
        fmt.Println(d5)
 
        d6 := decimal.NewFromFloatWithExponent(123.456, -3)
        fmt.Println(d6)
 
        d7 := decimal.NewFromFloatWithExponent(123.456, -4)
        fmt.Println(d7)
}

Zajímavé bude sledovat, jak ovlivní předaná hodnota exponentu hodnotu, která převodem na typ Decimal vznikne:

123
120
100
0
123.5
123.46
123.456
123.456

Kladné hodnoty znamenají, že se obecně ztrácí informace o cifrách před desetinnou čárkou/tečkou a záporné hodnoty mohou ovlivnit přesnost za desetinnou čárkou/tečkou. Je to vlastně logické, když si uvědomíme, že hodnota je reprezentována jako celé_číslo×10exponent. Tj. když například budeme vyžadovat použití exponentu==1, bude muset být základ nastaven na 12 a exponent na jedničku (tj. poslední cifra se ztratí).

8. Převod obsahu řetězce na numerickou hodnotu typu Decimal

Velmi často má programátor k dispozici numerickou hodnotu v textové podobě, tj. reprezentovanou řetězcem. Takovou hodnotu lze převést (naparsovat) na typ Decimal přímo, tj. bez mezipřevodu na celá čísla nebo čísla s plovoucí řádovou čárkou. V tom nejjednodušším případě můžeme pro parsing textu na Decimal použít konstruktor nazvaný NewFromString, který ovšem vrací dvě hodnoty – převedené číslo a rozhraní reprezentující chybu při převodu. Pochopitelně pokud k chybě nenastane, bude hodnota chyby nastavena na nil:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0, err := decimal.NewFromString("-0")
        fmt.Println(d0, err)
        fmt.Println()
 
        d1, err := decimal.NewFromString("-1234567890.123456789")
        fmt.Println(d1, err)
        fmt.Println()
 
        d2, err := decimal.NewFromString("1e1000")
        fmt.Println(d2, err)
        fmt.Println()
 
        d3, err := decimal.NewFromString("1e-1000")
        fmt.Println(d3, err)
        fmt.Println()
 
        d4, err := decimal.NewFromString("-1234567890e123456")
        fmt.Println(d4, err)
        fmt.Println()
}

A takto vypadají výsledky (povšimněte si, že chyba je v těchto případech vždy nil):

0 <nil>
 
-1234567890.123456789 <nil>
 
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 <nil>
 
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 <nil>
 
...
...
...
Poznámka: poslední hodnota je obrovská, a proto ani není vypsána.

Otestovat si můžeme i pokusy o převod řetězců, které číselné hodnoty (v textové podobě) ve skutečnosti neobsahují:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0, err := decimal.NewFromString("-a")
        fmt.Println(d0, err)
        fmt.Println()
 
        d1, err := decimal.NewFromString("-x.123456789")
        fmt.Println(d1, err)
        fmt.Println()
 
        d2, err := decimal.NewFromString("1e100a")
        fmt.Println(d2, err)
        fmt.Println()
}

V těchto případech získáme chyby s přesnější informací o provedené operaci a problémech, které nastaly:

0 can't convert -a to decimal
 
0 can't convert -x.123456789 to decimal
 
0 can't convert 1e100a to decimal: exponent is not numeric

9. Automatická kontrola, zda naparsování hodnoty z řetězce bylo úspěšné

Explicitní kontrola chyby, která může nastat při parsování řetězce, je v jazyce Go (bez podpory syntaxe pro zachytávání výjimek) problematická, resp. přesněji řečeno poněkud nečitelná. Proto se ustálil idiom, že konstruktory popř. jiné funkce existují i ve variantě typicky začínající prefixem Must. Taková varianta nevrací chybovou hodnotu, ale v případě chyby přímo havaruje, tj. vyvolá panic. V knihovně decimal lze při parsingu řetězců použít konstruktor nazvaný RequireFromString (zde není onen idiom dodržen), který se používá následovně:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        d0 := decimal.RequireFromString("-a")
        fmt.Println(d0)
        fmt.Println()
 
        d1 := decimal.RequireFromString("-x.123456789")
        fmt.Println(d1)
        fmt.Println()
 
        d2 := decimal.RequireFromString("1e100a")
        fmt.Println(d2)
        fmt.Println()
}

Tento program po svém překladu a spuštění zhavaruje při pokusu o parsing prvního řetězce, takže se další konverze již nebudou provádět:

panic: can't convert -a to decimal
 
goroutine 1 [running]:
github.com/shopspring/decimal.RequireFromString(...)
        /home/ptisnovs/go/pkg/mod/github.com/shopspring/decimal@v1.3.1/decimal.go:243
main.main()
        /home/ptisnovs/src/go-root/article_AA/12_require_from_string/require_from_string_err.go:10 +0x208
exit status 2

10. Naparsování hodnoty z řetězce se specifikací nadbytečných znaků v řetězci

Mnohdy se taktéž setkáme se situací, kdy vstupní řetězce obsahují nějaké nadbytečné znaky, například oddělovače milionů a tisíců (v tuzemsku mezery, v US pak čárky), symboly měn atd. Tyto znaky je samozřejmě možné explicitně odstranit ještě před konverzí (parsingem) řetězce na hodnotu typu Decimal. Ovšem díky existenci konstruktoru NewFromFormattedString je možné jak odstranění, tak i konverzi provést v jediném kroku. Tomuto konstruktoru se předává jak řetězec, který se má parsovat, tak i regulární výraz (a ten je mimochodem konstruován právě konstruktorem, jehož jméno začíná na Must).

Nejprve si ukažme parsing řetězců obsahujících přímo textovou reprezentaci čísla, z níž se žádné znaky nemusí odstraňovat:

package main
 
import (
        "fmt"
        "regexp"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        r := regexp.MustCompile("neco")
 
        d0, err := decimal.NewFromFormattedString("-0", r)
        fmt.Println(d0, err)
        fmt.Println()
 
        d1, err := decimal.NewFromFormattedString("-1234567890.123456789", r)
        fmt.Println(d1, err)
        fmt.Println()
 
        d2, err := decimal.NewFromFormattedString("1e1000", r)
        fmt.Println(d2, err)
        fmt.Println()
 
        d3, err := decimal.NewFromFormattedString("1e-1000", r)
        fmt.Println(d3, err)
        fmt.Println()
 
        d4, err := decimal.NewFromFormattedString("-1234567890e123", r)
        fmt.Println(d4, err)
        fmt.Println()
}

Výsledky:

0 <nil>
 
-1234567890.123456789 <nil>
 
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 <nil>
 
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 <nil>
 
-1234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 <nil>

V dalším příkladu pak parsujeme řetězce, které obsahují „nechtěné“ znaky, které jsou odstraněny:

package main
 
import (
        "fmt"
        "regexp"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        r := regexp.MustCompile(",")
 
        d0, err := decimal.NewFromFormattedString("1,000", r)
        fmt.Println(d0, err)
        fmt.Println()
 
        d1, err := decimal.NewFromFormattedString("1,000,000", r)
        fmt.Println(d1, err)
        fmt.Println()
 
        d2, err := decimal.NewFromFormattedString("1.000", r)
        fmt.Println(d2, err)
        fmt.Println()
 
        d3, err := decimal.NewFromFormattedString("1,000.000", r)
        fmt.Println(d3, err)
        fmt.Println()
}

Výsledky by opět měly odpovídat očekávání:

1000 <nil>
 
1000000 <nil>
 
1 <nil>
 
1000 <nil>

Odstranění symbolů měny:

package main
 
import (
        "fmt"
        "regexp"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        r := regexp.MustCompile("\\$")
 
        d0, err := decimal.NewFromFormattedString("$1000", r)
        fmt.Println(d0, err)
        fmt.Println()
 
        d1, err := decimal.NewFromFormattedString("20.25$", r)
        fmt.Println(d1, err)
        fmt.Println()
 
        r = regexp.MustCompile("Kč")
        d2, err := decimal.NewFromFormattedString("1000Kč", r)
        fmt.Println(d2, err)
        fmt.Println()
}

Vypočtené a zobrazené numerické hodnoty:

1000 <nil>
 
20.25 <nil>
 
1000 <nil>

11. Základní aritmetické operace s hodnotami typu Decimal

Datový typ Decimal pochopitelně obsahuje i podporu pro provádění všech pěti základních aritmetických operací (součet, rozdíl, součin, podíl a modulo). Tyto operace se zapisují odlišným způsobem, než je tomu u typů ze základní knihovny big, protože příjemcem je první hodnota a jediným parametrem operace druhá numerická hodnota:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        x := decimal.NewFromInt32(2)
 
        y := decimal.NewFromInt32(3)
 
        r1 := x.Add(y)
        r2 := x.Sub(y)
        r3 := x.Mul(y)
        r4 := x.Div(y)
        r5 := x.Mod(y)
 
        fmt.Println("x   ", x)
        fmt.Println("y   ", y)
        fmt.Println("x+y ", r1)
        fmt.Println("x-y ", r2)
        fmt.Println("x*y ", r3)
        fmt.Println("x/y ", r4)
        fmt.Println("x%y ", r5)
}

Výsledky získané po překladu a spuštění tohoto demonstračního příkladu by neměly být příliš překvapující (snad až na zdánlivě omezenou přesnost hodnoty 2/3):

x    2
y    3
x+y  5
x-y  -1
x*y  6
x/y  0.6666666666666667
x%y  2

12. Podíl se specifikací způsobu zaokrouhlení výsledku

Již v předchozí kapitole jsme mohli vidět, že u operace podílu nebyl (a mnohdy ani nemůže být) výsledek zcela přesný. Zajímavé je, že u datového typu Decimal lze přesnost této aritmetické operace zvolit, pokud se namísto metody Div použije metoda nazvaná DivRound, které se navíc předá i další hodnota (typu int32 – viz druhou kapitolu) s požadovanou přesností:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        x := decimal.NewFromInt32(1)
 
        y := decimal.NewFromInt32(7)
 
        for precision := 30; precision > 0; precision-- {
                r := x.DivRound(y, int32(precision))
                fmt.Println(r)
        }
}

Výsledky zobrazené tímto demonstračním příkladem ukazují výpočty podílu s přesností klesající od 30 do 1:

0.142857142857142857142857142857
0.14285714285714285714285714286
0.1428571428571428571428571429
0.142857142857142857142857143
0.14285714285714285714285714
0.1428571428571428571428571
0.142857142857142857142857
0.14285714285714285714286
0.1428571428571428571429
0.142857142857142857143
0.14285714285714285714
0.1428571428571428571
0.142857142857142857
0.14285714285714286
0.1428571428571429
0.142857142857143
0.14285714285714
0.1428571428571
0.142857142857
0.14285714286
0.1428571429
0.142857143
0.14285714
0.1428571
0.142857
0.14286
0.1429
0.143
0.14
0.1

13. Výpočet faktoriálu s hodnotami typu Decimal

V předchozích dvou článcích s popisem datových typů nabízených standardní knihovnou jazyka Go big jsme si ukazovali některé základní numerické algoritmy, například výpočet konstanty π či výpočet faktoriálu. A právě výpočet faktoriálu nad hodnotami typu Decimal je ukázán v této kapitole. I když je opět nutné nahradit zápis aritmetických operací (i operace relační) za volání metod, je výsledek nepatrně čitelnější, než je tomu v případě použití big.Int, a to kvůli odlišné sémantice volání binárních operací:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func factorial(n decimal.Decimal) decimal.Decimal {
        one := decimal.NewFromInt32(1)
        var zero decimal.Decimal
 
        if n.Cmp(zero) <= 0 {
                return one
        }
        return n.Mul(factorial(n.Sub(one)))
}
 
func main() {
        for n := int64(1); n < 80; n++ {
                f := factorial(decimal.NewFromInt(n))
                fmt.Printf("%3d! = %s\n", n, f)
        }
}

Hodnoty faktoriálu vypočtené tímto demonstračním příkladem:

  1! = 1
  2! = 2
  3! = 6
  4! = 24
  5! = 120
  6! = 720
  7! = 5040
  8! = 40320
  9! = 362880
 10! = 3628800
 ...
 ...
 ...
 70! = 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000
 71! = 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000
 72! = 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000
 73! = 4470115461512684340891257138125051110076800700282905015819080092370422104067183317016903680000000000000000
 74! = 330788544151938641225953028221253782145683251820934971170611926835411235700971565459250872320000000000000000
 75! = 24809140811395398091946477116594033660926243886570122837795894512655842677572867409443815424000000000000000000
 76! = 1885494701666050254987932260861146558230394535379329335672487982961844043495537923117729972224000000000000000000
 77! = 145183092028285869634070784086308284983740379224208358846781574688061991349156420080065207861248000000000000000000
 78! = 11324281178206297831457521158732046228731749579488251990048962825668835325234200766245086213177344000000000000000000
 79! = 894618213078297528685144171539831652069808216779571907213868063227837990693501860533361810841010176000000000000000000

14. Agregační funkce s hodnotami typu Decimal

Velmi užitečné mohou být i agregační funkce nazvané Min, Max, Avg a Sum, jejichž jména dostatečně popisují prováděný výpočet. Těmto funkcím je možné předat libovolný (nenulový) počet hodnot typu Decimal a výsledkem bude nová hodnota stejného typu s vypočteným výsledkem:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        x := decimal.NewFromInt32(2)
        y := decimal.NewFromInt32(3)
        z := decimal.NewFromInt32(1)
        w := decimal.NewFromInt32(0)
 
        min := decimal.Min(x, y, z, w)
        max := decimal.Max(x, y, z, w)
        avg := decimal.Avg(x, y, z, w)
        sum := decimal.Sum(x, y, z, w)
 
        fmt.Println("min   ", min)
        fmt.Println("max   ", max)
        fmt.Println("avg   ", avg)
        fmt.Println("sum   ", sum)
}

Podívejme se nyní na vypočtené výsledky:

min    0
max    3
avg    1.5
sum    6

Pokus o předání nulového počtu parametrů skončí chybou při překladu:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        min := decimal.Min()
        max := decimal.Max()
        avg := decimal.Avg()
        sum := decimal.Sum()
 
        fmt.Println("min   ", min)
        fmt.Println("max   ", max)
        fmt.Println("avg   ", avg)
        fmt.Println("sum   ", sum)
}
./g.go:10:9: not enough arguments in call to decimal.Min
        have ()
        want (decimal.Decimal, ...decimal.Decimal)
./g.go:11:9: not enough arguments in call to decimal.Max
        have ()
        want (decimal.Decimal, ...decimal.Decimal)
./g.go:12:9: not enough arguments in call to decimal.Avg
        have ()
        want (decimal.Decimal, ...decimal.Decimal)
./g.go:13:9: not enough arguments in call to decimal.Sum
        have ()
        want (decimal.Decimal, ...decimal.Decimal)

15. Interní struktura hodnot typu Decimal

Ve druhé kapitole jsme si ukázali datovou strukturu, která je použita pro uchování hodnot typu Decimal. Tento datový typ podporuje i serializaci hodnoty do sekvence bajtů, což nám umožní sledovat konkrétní způsob uložení numerických hodnot. Pro serializaci do sekvence bajtů se používá metoda nazvaná MarshalBinary, která vrací řez bajtů (slice) a taktéž informaci o chybě, ke které (čistě teoreticky) může dojít (prakticky k chybě nedojde, ovšem musí být splněno rozhraní BinaryMarshaller).

V dalším demonstračním příkladu se pokusíme o serializaci několika hodnot a prozkoumáme výsledky:

package main
 
import (
        "fmt"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        values := []float32{0, 1, -1, 10, -10, 100, 255, 256, 257, 2550, 2560, 2570, 1000, 1e38, -1e38}
 
        for _, value := range values {
                dec := decimal.NewFromFloat32(float32(value))
                bin, _ := dec.MarshalBinary()
                fmt.Println(dec, "\t", bin)
        }
}

Velmi zajímavý je pohled na zobrazené výsledky:

0        [0 0 0 0 2]
1        [0 0 0 0 2 1]
-1       [0 0 0 0 3 1]
10       [0 0 0 1 2 1]
-10      [0 0 0 1 3 1]
100      [0 0 0 2 2 1]
255      [0 0 0 0 2 255]
256      [0 0 0 0 2 1 0]
257      [0 0 0 0 2 1 1]
2550     [0 0 0 1 2 255]
2560     [0 0 0 1 2 1 0]
2570     [0 0 0 1 2 1 1]
1000     [0 0 0 3 2 1]
100000000000000000000000000000000000000          [0 0 0 38 2 1]
-100000000000000000000000000000000000000         [0 0 0 38 3 1]

Nula je, jak je patrné, uložena bez mantisy.

Musíme si uvědomit, že ve vypsané sekvenci bajtů první čtyři bajty odpovídají desítkovému exponentu, což vysvětluje rozdíly mezi těmito hodnotami:

1        [0 0 0 0 2 1]
10       [0 0 0 1 2 1]
100      [0 0 0 2 2 1]
1000     [0 0 0 3 2 1]
100000000000000000000000000000000000000          [0 0 0 38 2 1]

Povšimněte si, že poslední dva bajty (což je, když poněkud předběhneme, znaménko a mantisa) jsou shodné, zatímco exponent je postupně roven 0, 1, 2 a 3, což přesně odpovídá reprezentované hodnotě.

Dále lze snadno zjistit, který bit v pátém bajtu odpovídá znaménku:

1        [0 0 0 0 2 1]
-1       [0 0 0 0 3 1]
10       [0 0 0 1 2 1]
-10      [0 0 0 1 3 1]
100000000000000000000000000000000000000          [0 0 0 38 2 1]
-100000000000000000000000000000000000000         [0 0 0 38 3 1]

A konečně hodnota mantisy je uložena binárně:

255      [0 0 0 0 2 255] // 255
256      [0 0 0 0 2 1 0] // 1*256+0
257      [0 0 0 0 2 1 1] // i*255+1
2550     [0 0 0 1 2 255] // 10*(255)
2560     [0 0 0 1 2 1 0] // 10*(1*256+0)
2570     [0 0 0 1 2 1 1] // 10*(1*256+0)

16. Výpočet hodnoty π s typem Decimal

Opět se na chvíli vraťme k předchozím dvěma článkům, v nichž jsme si popisovali datové typy poskytované standardní knihovnou big, tj. typy big.Int, big.Rat a big.Float. V těchto článcích jsme mj. ukazovali i (pomalý) postupný výpočet konstanty π založený na Wallisově produktu (řadě členů, které se nesčítají, ale násobí). Tento algoritmus je pochopitelně možné přepsat do podoby, v níž budou pro výpočty použity hodnoty typu Decimal. A opět budeme zkoumat absolutní a relativní chybu těchto výpočtů při porovnání výsledků s „přesnou“ (ehm) reprezentací π ve formě konstanty math.Pi:

package main
 
import (
        "fmt"
        "math"
 
        "github.com/shopspring/decimal"
)
 
func main() {
        result := decimal.NewFromInt32(2)
 
        one := decimal.NewFromInt32(1)
        limit := decimal.NewFromInt32(200)
 
        for n := decimal.NewFromInt32(1); n.Cmp(limit) <= 0; n = n.Add(one) {
                m := decimal.NewFromInt32(4)
                m = m.Mul(n)
                m = m.Mul(n)
                mn := m.Sub(one)
 
                m = m.Div(mn)
 
                result = result.Mul(m)
 
                f, _ := result.Float64()
                absError := math.Pi - f
                relError := 100.0 * absError / math.Pi
                fmt.Println(f, "\t", absError, "\t", relError) //, "\t", result.String())
        }
}

A vypočtené výsledky:

2.6666666666666665       0.4749259869231266      15.11736368432249
2.8444444444444446       0.29714820914534856     9.458521263277312
2.9257142857142857       0.2158783678755074      6.871621870799526
2.972154195011338        0.1694384585784552      5.3933936465264996
3.002175954556907        0.139416699032886       4.437771360127774
3.023170192001361        0.11842246158843217     3.7695040269818167
...
...
...
3.1227247411658112       0.018867912423981892    0.6005843056203406
3.1231673669264297       0.018425286663363405    0.5864950900718922
3.1235897019320995       0.01800295165769361     0.573051749313274
3.1239931101333047       0.017599543456488398    0.56021086745215
...
...
...
3.1376384825154533       0.003954171074339818    0.1258651744624345
3.137658290464056        0.003934363125737139    0.12523466787590917
3.137677900950937        0.0039147526388561005   0.12461044669119796
Poznámka: konvergence k π je pochopitelně opět velmi pomalá.

17. Rychlost výpočtů s hodnotami typu Decimal

Prozatím jsme si ukázali, jak jsou hodnoty typu Decimal interně uloženy a jaké operace je s nimi možné provádět. Ovšem důležitá je i rychlost prováděných výpočtů. Ta pochopitelně nemůže dosahovat rychlosti operací s hodnotami, jejichž formát odpovídá použité architektuře mikroprocesorů (tedy typicky byte, int s různou šířkou, float32 a float64). Pro zajímavost si porovnáme rychlost výpočtu konstanty π výše zmíněným Wallisovým produktem. Počítat přitom budeme dvěma způsoby – s využitím standardní knihovny math/big a taktéž s využitím knihovny shopspring/decimal. Povšimněte si, že u typu Decimal jsou výpočty zapsány mnohem jednodušším způsobem, bez nutnosti převodu mezi různými datovými typy atd.:

package main
 
import (
        "math"
        "math/big"
 
        "testing"
 
        "github.com/shopspring/decimal"
)
 
func piBig(n int) float64 {
        var result big.Rat
 
        result.SetFrac64(2, 1)
 
        one := big.NewInt(1)
        limit := big.NewInt(1000)
 
        for n := big.NewInt(1); n.Cmp(limit) <= 0; n.Add(n, one) {
                m := big.NewInt(4)
                m.Mul(m, n)
                m.Mul(m, n)
                mn := big.NewInt(0)
                mn.Sub(m, one)
 
                var item big.Rat
                item.SetFrac(m, mn)
                result.Mul(&result, &item)
 
        }
        f, _ := result.Float64()
        return f
}
 
func piDecimal(n int) float64 {
        result := decimal.NewFromInt32(2)
 
        one := decimal.NewFromInt32(1)
        limit := decimal.NewFromInt32(200)
 
        for n := decimal.NewFromInt32(1); n.Cmp(limit) <= 0; n = n.Add(one) {
                m := decimal.NewFromInt32(4)
                m = m.Mul(n)
                m = m.Mul(n)
                mn := m.Sub(one)
 
                m = m.Div(mn)
 
                result = result.Mul(m)
        }
        f, _ := result.Float64()
        return f
}
 
func BenchmarkPiBig(b *testing.B) {
        for n := 1; n < b.N; n++ {
                f := piBig(n)
                if math.Abs(f-math.Pi) >= 0.01 {
                        b.Fatal("Incorrect result", f)
                }
        }
}
 
func BenchmarkPiDecimal(b *testing.B) {
        for n := 1; n < b.N; n++ {
                f := piDecimal(n)
                if math.Abs(f-math.Pi) >= 0.01 {
                        b.Fatal("Incorrect result", f)
                }
        }
}
Poznámka: kontrola, zda je vypočtená hodnota korektní, je provedena dosti naivním způsobem, protože přesnost by se měla zvyšovat spolu s rostoucím počtem členů Wallisova produktu.

18. Výsledky benchmarků

Benchmark představený v předchozí kapitole nyní přeložíme a následně i spustíme:

root_podpora

$ ./run_benchmark.sh

Z výsledků je patrné, že výpočet s hodnotami Decimal je v tomto případě mnohem rychlejší, než je tomu u datového typu big.Float, což se ovšem dalo předpokládat vzhledem ke konverzím numerických hodnot v prvním benchmarku a taktéž kvůli složitější interní struktuře datového typu big.Float (na druhou stranu je rozdíl v rozsahu dvou řádů):

goos: linux
goarch: amd64
pkg: div-round
cpu: Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz
BenchmarkPiBig-8            1000          26720122 ns/op
BenchmarkPiDecimal-8        1000            412662 ns/op
PASS
ok      div-round       27.136s

19. Repositář s demonstračními příklady

Zdrojové kódy všech předminule, minule i dnes použitých demonstračních příkladů naprogramovaných v jazyku Go byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root. V případě, že nebudete chtít klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad/soubor Stručný popis Cesta
1 01_bigint_construction.go konstrukce instance datového typu big.Int a tisk uložené hodnoty https://github.com/tisnik/go-root/blob/master/article_A8/01_bi­gint_construction.go
2 02_bigint_add.go aritmetická operace součtu a datový typ big.Int https://github.com/tisnik/go-root/blob/master/article_A8/02_bi­gint_add.go
3 03_bigint_large_numbers.go aritmetická operace součinu a datový typ big.Int https://github.com/tisnik/go-root/blob/master/article_A8/03_bi­gint_large_numbers.go
4 04_factorial.go výpočet faktoriálu s využitím datového typu big.Int https://github.com/tisnik/go-root/blob/master/article_A8/04_fac­torial.go
5 04_factorial_B.go zjednodušený výpočet faktoriálu s využitím datového typu big.Int https://github.com/tisnik/go-root/blob/master/article_A8/04_fac­torial_B.go
6 05_bigint_print_base.go převod hodnoty typu big.Int na text s volitelnou bází https://github.com/tisnik/go-root/blob/master/article_A8/05_bi­gint_print_base.go
7 06_bigint_as_bytes.go zobrazení interní struktury hodnot typu big.Int https://github.com/tisnik/go-root/blob/master/article_A8/06_bi­gint_as_bytes.go
8 07_bigint_change_raw.go modifikace interní struktury hodnot typu big.Int https://github.com/tisnik/go-root/blob/master/article_A8/07_bi­gint_change_raw.go
9 08_bigint_change_raw.go nastavení jednotlivých bajtů hodnoty typu big.Int https://github.com/tisnik/go-root/blob/master/article_A8/08_bi­gint_change_raw.go
10 09_bigint_from_string.go konstrukce hodnoty big.Int z řetězce https://github.com/tisnik/go-root/blob/master/article_A8/09_bi­gint_from_string.go
       
11 10_rationals_construction.go konstrukce zlomku – hodnoty big.Rat https://github.com/tisnik/go-root/blob/master/article_A8/10_ra­tionals_construction.go
12 11_rationals_add.go součet dvou zlomků https://github.com/tisnik/go-root/blob/master/article_A8/11_ra­tionals_add.go
13 12_rationals_mul.go operace součinu zlomků a vliv na přesnost a rozsah hodnoty https://github.com/tisnik/go-root/blob/master/article_A8/12_ra­tionals_mul.go
14 13_rational_to_int.go převod zlomku na celočíselného čitatele a jmenovatele https://github.com/tisnik/go-root/blob/master/article_A8/13_ra­tional_to_int.go
15 14_rationals_to_float.go převod zlomku na hodnotu s plovoucí řádovou čárkou https://github.com/tisnik/go-root/blob/master/article_A8/14_ra­tionals_to_float.go
16 15_pi_wallis_product.go výpočet hodnoty π (naivní varianta) https://github.com/tisnik/go-root/blob/master/article_A8/15_pi_wa­llis_product.go
17 16_pi_wallis_product_limits.go limity naivní varianty výpočtu hodnoty π https://github.com/tisnik/go-root/blob/master/article_A8/16_pi_wa­llis_product_limits.go
18 17_pi_better_wallis_product.go vylepšená varianta výpočtu hodnoty π https://github.com/tisnik/go-root/blob/master/article_A8/17_pi_bet­ter_wallis_product.go
19 18_rationals_div_zero.go konstrukce zlomku s nulovým jmenovatelem https://github.com/tisnik/go-root/blob/master/article_A8/18_ra­tionals_div_zero.go
20 19_rationals_div_zero.go operace, která vytvoří zlomek s nulovým jmenovatelem https://github.com/tisnik/go-root/blob/master/article_A8/19_ra­tionals_div_zero.go
       
21 01_bigfloat_construction.go konstrukce hodnoty typu big.Float, základní aritmetické operace a zobrazení hodnoty https://github.com/tisnik/go-root/blob/master/article_A9/01_big­float_construction.go
22 02_bigfloat_add.go zvýšení počtu cifer zobrazených za desetinnou čárkou (tečkou) https://github.com/tisnik/go-root/blob/master/article_A9/02_big­float_add.go
23 03_bigfloat_large_numbers.go zobrazení velkých hodnot bez použití exponentu https://github.com/tisnik/go-root/blob/master/article_A9/03_big­float_large_numbers.go
24 04_bigfloat_small_numbers.go zobrazení malých hodnot bez použití exponentu https://github.com/tisnik/go-root/blob/master/article_A9/04_big­float_small_numbers.go
25 05_bigfloat_exp_format.go zobrazení numerických hodnot ve tvaru s exponentem https://github.com/tisnik/go-root/blob/master/article_A9/05_big­float_exp_format.go
26 06_bigfloat_exp_format.go zobrazení numerických hodnot ve tvaru s exponentem https://github.com/tisnik/go-root/blob/master/article_A9/06_big­float_exp_format.go
27 07_hexa_mantissa.go výpis hodnoty s využitím mantisy zapsané v hexadecimálním kódu https://github.com/tisnik/go-root/blob/master/article_A9/07_he­xa_mantissa.go
28 08_pi_wallis_product.go výpočet hodnoty π podruhé https://github.com/tisnik/go-root/blob/master/article_A9/08_pi_wa­llis_product.go
29 09_bigfloat_to_rat.go https://github.com/tisnik/go-root/blob/master/article_A9/09_big­float_to_rat.go
30 10_bigfloat_to_float64.go převody mezi typem big.Float a zlomky https://github.com/tisnik/go-root/blob/master/article_A9/10_big­float_to_float64.go
31 11_bigfloat_to_float32.go převod mezi typem big.Float a typy float64 a float32 https://github.com/tisnik/go-root/blob/master/article_A9/11_big­float_to_float32.go
32 12_precision.go přesnost hodnot typu big.Float https://github.com/tisnik/go-root/blob/master/article_A9/12_pre­cision.go
33 13_precision_B.go přesnost hodnot typu big.Float https://github.com/tisnik/go-root/blob/master/article_A9/13_pre­cision_B.go
34 14_positive_infinity.go výsledek podílu 1/0 https://github.com/tisnik/go-root/blob/master/article_A9/14_po­sitive_infinity.go
35 15_negative_infinity.go výsledek podílu –1/0 https://github.com/tisnik/go-root/blob/master/article_A9/15_ne­gative_infinity.go
36 16_inf_operations.go povolené operace s nekonečny https://github.com/tisnik/go-root/blob/master/article_A9/16_in­f_operations.go
37 17_inf_operations.go nepovolené operace s nekonečny: -∞+∞ https://github.com/tisnik/go-root/blob/master/article_A9/17_in­f_operations.go
38 18_inf_operations.go nepovolené operace s nekonečny: ∞×0 https://github.com/tisnik/go-root/blob/master/article_A9/18_in­f_operations.go
39 19_catch_panic.go zachycení nepovolené operace s nekonečny https://github.com/tisnik/go-root/blob/master/article_A9/19_cat­ch_panic.go
40 20_div_by_zero.go podíl 0/0 https://github.com/tisnik/go-root/blob/master/article_A9/20_div_by_ze­ro.go
       
41 01_zero_value nulová hodnota datového typu Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/01_zero_value
42 02_construct_from_int32 konstrukce hodnoty typu Decimal z celého čísla typu int32 https://github.com/tisnik/go-root/blob/master/article_A­A/02_construct_from_int32
43 03_construct_from_int konstrukce hodnoty typu Decimal z celého čísla typu int https://github.com/tisnik/go-root/blob/master/article_A­A/03_construct_from_int
44 04_construct_from_float32 konstrukce hodnoty typu Decimal z čísla typu float32 https://github.com/tisnik/go-root/blob/master/article_A­A/04_construct_from_float32
45 05_construct_from_float konstrukce hodnoty typu Decimal z čísla typu float64 https://github.com/tisnik/go-root/blob/master/article_A­A/05_construct_from_float
46 06_construct_from_float_with_exponent konstrukce hodnoty typu Decimal z čísla typu float64 se specifikací exponentu https://github.com/tisnik/go-root/blob/master/article_A­A/06_construct_from_float_wit­h_exponent
47 07_construct_from_string naparsování hodnoty typu Decimal z řetězce https://github.com/tisnik/go-root/blob/master/article_A­A/07_construct_from_string
48 08_construct_from_formatted_string naparsování hodnoty typu Decimal z naformátovaného řetězce https://github.com/tisnik/go-root/blob/master/article_A­A/08_construct_from_format­ted_string
49 09_construct_from_formatted_string naparsování hodnoty typu Decimal z naformátovaného řetězce https://github.com/tisnik/go-root/blob/master/article_A­A/09_construct_from_format­ted_string
50 10_construct_from_formatted_string naparsování hodnoty typu Decimal z naformátovaného řetězce https://github.com/tisnik/go-root/blob/master/article_A­A/10_construct_from_format­ted_string
51 11_construct_from_string_err naparsování hodnoty typu Decimal z řetězce, který neobsahuje číselnou hodnotu https://github.com/tisnik/go-root/blob/master/article_A­A/11_construct_from_strin­g_err
52 12_require_from_string naparsování hodnoty typu Decimal z řetězce, který neobsahuje číselnou hodnotu https://github.com/tisnik/go-root/blob/master/article_A­A/12_require_from_string
53 13_arithmetic základní aritmetické operace s hodnotami typu Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/13_arithmetic
54 14_div_round podíl se specifikací způsobu zaokrouhlení výsledku https://github.com/tisnik/go-root/blob/master/article_A­A/14_div_round
55 15_factorial výpočet faktoriálu s hodnotami typu Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/15_factorial
56 16_aggregation agregační funkce s hodnotami typu Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/16_aggregation
57 17_internals interní struktura hodnot typu Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/17_internals
58 18_pi_wallis_product výpočet konstanty π s typem Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/18_pi_wallis_product
       
59 19_benchmark benchmark: výpočet konstanty π s typem Decimal https://github.com/tisnik/go-root/blob/master/article_A­A/19_benchmark

20. Odkazy na Internetu

  1. Číselné hodnoty s neomezeným rozsahem a přesností v programovacím jazyku Go (1)
    https://www.root.cz/clanky/ciselne-hodnoty-s-neomezenym-rozsahem-a-presnosti-v-programovacim-jazyku-go-1/
  2. Číselné hodnoty s neomezeným rozsahem a přesností v programovacím jazyku Go (2)
    https://www.root.cz/clanky/ciselne-hodnoty-s-neomezenym-rozsahem-a-presnosti-v-programovacim-jazyku-go-2/
  3. Balíček big pro jazyk Go
    https://pkg.go.dev/math/big
  4. Zdrojové kódu pro balíček big
    https://cs.opensource.goo­gle/go/go/+/master:src/mat­h/big/
  5. Arbitrary-precision arithmetic
    https://en.wikipedia.org/wi­ki/Arbitrary-precision_arithmetic
  6. Floating-point error mitigation
    https://en.wikipedia.org/wiki/Floating-point_error_mitigation
  7. Beating Floating Point at its Own Game: Posit Arithmetic
    http://www.johngustafson.net/pdfs/Be­atingFloatingPoint.pdf
  8. Unum (number format)
    https://en.wikipedia.org/wi­ki/Unum_(number_format)
  9. The GNU MPFR Library
    https://www.mpfr.org/
  10. GMP: Arithmetic without limitations
    https://gmplib.org/
  11. GNU MP 6.2.1 manual
    https://gmplib.org/manual/index
  12. Anatomy of a posit number
    https://www.johndcook.com/blog/2018/04/11/a­natomy-of-a-posit-number/
  13. Better floating point: posits in plain language
    http://loyc.net/2019/unum-posits.html
  14. Posits, a New Kind of Number, Improves the Math of AI: The first posit-based processor core gave a ten-thousandfold accuracy boost
    https://spectrum.ieee.org/floating-point-numbers-posits-processor
  15. Posit Standard Document (2022)
    https://posithub.org/khub_widget
  16. Standard for Posit™ Arithmetic (2022)
    https://posithub.org/docs/po­sit_standard-2.pdf
  17. Posit Calculator
    https://posithub.org/widget/cal­culator/
  18. SoftPosit
    https://gitlab.com/cerlane/SoftPosit
  19. PySigmoid
    https://github.com/mighty­mercado/PySigmoid
  20. sgpositpy
    https://github.com/xman/sgpositpy
  21. SoftPosit.jl
    https://github.com/milankl/Sof­tPosit.jl
  22. SigmoidNumbers.jl
    https://github.com/MohHiz­zani/SigmoidNumbers.jl
  23. How many digits can float8, float16, float32, float64, and float128 contain?
    https://stackoverflow.com/qu­estions/56514892/how-many-digits-can-float8-float16-float32-float64-and-float128-contain
  24. 15. Floating Point Arithmetic: Issues and Limitations (Python documentation)
    https://docs.python.org/3/tu­torial/floatingpoint.html
  25. Number limits, overflow, and roundoff
    https://www.khanacademy.or­g/computing/computers-and-internet/xcae6f4a7ff015e7d:digital-information/xcae6f4a7ff015e7d:li­mitations-of-storing-numbers/a/number-limits-overflow-and-roundoff
  26. The upper and lower limits of IEEE-754 standard
    https://math.stackexchange­.com/questions/2607697/the-upper-and-lower-limits-of-ieee-754-standard

Byl pro vás článek přínosný?