{"id":13413738,"url":"https://github.com/db47h/decimal","last_synced_at":"2026-01-12T10:27:29.030Z","repository":{"id":46375262,"uuid":"267355587","full_name":"db47h/decimal","owner":"db47h","description":"An arbitrary-precision decimal floating-point arithmetic package for Go","archived":false,"fork":false,"pushed_at":"2022-08-12T00:13:53.000Z","size":466,"stargazers_count":40,"open_issues_count":0,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-07-31T20:52:48.696Z","etag":null,"topics":["arbitrary-precision","bignum","decimal","financial","floating-point","go"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/db47h.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":null,"patreon":"db47h","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-05-27T15:23:59.000Z","updated_at":"2024-07-20T13:36:40.000Z","dependencies_parsed_at":"2022-07-19T22:04:58.471Z","dependency_job_id":null,"html_url":"https://github.com/db47h/decimal","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/db47h%2Fdecimal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/db47h%2Fdecimal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/db47h%2Fdecimal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/db47h%2Fdecimal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/db47h","download_url":"https://codeload.github.com/db47h/decimal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243635804,"owners_count":20323000,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arbitrary-precision","bignum","decimal","financial","floating-point","go"],"created_at":"2024-07-30T20:01:47.817Z","updated_at":"2026-01-12T10:27:29.025Z","avatar_url":"https://github.com/db47h.png","language":"Go","readme":"# decimal\n\n[![godocb]][godoc]\n[![travisb]][travis]\n[![coverb]][cover]\n[![goreportb]][goreport]\n\nPackage decimal implements arbitrary-precision decimal floating-point arithmetic\nfor Go.\n\n## Rationale\n\nHow computers represent numbers internally is of no import for most\napplications. However numerical applications that interract with humans must use\nthe same number base as humans. A very simple example is this:\nhttps://play.golang.org/p/CVjiDCdhyoR where `1.1 + 0.11 = 1.2100000000000002`.\nNot quite what one would expect.\n\nThere are other arbitrary-precision decimal floating-point libraries available,\nbut most use a binray representation of the mantissa (building on top of\n`big.Int`), use external C libraries or are just plain slow.\n\nThis started out as an experiment on how an implementation using a pure decimal\nrepresentation would fare performance-wise. Secondary goals were to implement it\nwith an API that would match the Go standard library's math/big API (for better\nor worse), and build it in such a way that it could some day be integrated into\nthe Go standard library.\n\nAdmittedly, this actually started out while doing some yak shaving, combined\nwith an NIH syndrom, but the result was well worth it.\n\n## Features\n\nThe implementation is in essence a port of `big.Float` to decimal\nfloating-point, and the API is identical to that of `*big.Float` with the\nexception of a few additional getters and setters, an FMA operation, and helper\nfunctions to support implementation of missing low-level Decimal functionality\noutside this package.\n\nUnlike big.Float, the mantissa of a Decimal is stored in a little-endian Word\nslice as \"declets\" of 9 or 19 decimal digits per 32 or 64 bits Word. All\narithmetic operations are performed directly in base 10\\*\\*9 or 10\\*\\*19 without\nconversion to/from binary (see Performance below for a more in-depth discussion\nof this choice).\n\nWhile basic operations are slower than with a binary representation, some\noperations like rounding (happening after every other operation!), or aligning\nmantissae (in add/subtract) are much cheaper.\n\n### Decimal and IEEE-754\n\nThe decimal package supports IEEE-754 rounding modes, signed zeros, infinity,\nand an exactly rounded `Sqrt`. Other functions like Log will be implemented in a\nfuture \"math\" sub-package. All results are rounded to the desired precision (no\nmanual rounding).\n\nNaN values are not directly supported (like in `big.Float`). They can be seen as\n\"signaling NaNs\" in IEEE-754 terminology, that is when a NaN is generated as a\nresult of an operation, it causes a panic. Applications that need to handle NaNs\ngracefully can use Go's built-in panic/recover machanism to handle these\nefficiently: NaNs cause a panic with an ErrNaN which can be tested to\ndistinguish NaNs from other causes of panic.\n\nOn the other hand, the [context](https://pkg.go.dev/github.com/db47h/decimal/context?tab=doc)\nsub-package provides Contexts, which allow panic-free operation and a form of\nquiet-NaNs whereby any NaN generated by an operation will make the context enter\ninto an error state. Further operations with the context will be no-ops until\n(*Context).Err is called to check for errors.\n\nMantissae are always normalized, as a result, Decimals have a single possible\nrepresentation:\n\n    0.1 \u003c= mantissa \u003c 1; d = mantissa × 10**exponent\n\nso there is no notion of scale and no Quantize operation.\n\n## TODO's and upcoming features\n\n- Some math primitives are implemented in assembler. Right now only the amd64\n  version is implemented, so we're still missing i386, arm, mips, power, riscV,\n  and s390. The amd64 version could also use a good review (my assembly days\n  date back to the Motorola MC68000). HELP WANTED!\n- Complete decimal conversion tests\n- A math sub-package that will provide at least the functions required by\n  IEEE-754\n- Some performance improvement ideas:\n    - try a non-normalized mantissa.\n    - in add, there are some cycles to shave off by combining the shift and add\n      for simple cases (initial testing yielded mitigated results. Wait and see)\n\nThe decimal API is frozen, that is, any additional features will be added in\nsub-packages.\n\nWell, with the exception of `NewDecimal`: The current `integer mantissa` ×\n`10**exp`, works well enough, but I'm not truly happy with it. Early versions\nwere using a float64 value as initializer, but that lead to unexpected side\neffects where one would expect the number to be exact; not quite so as it turned\nout, so it's not an option either.\n\n## Performance\n\nThere are other full-featured arbitrary-precision decimal-floating point\nlibraries for Go out there, like [Eric Lagergren's decimal][eldecimal],\n[CockroachDB's apd][apd], or [Shopspring's decimal][Shopspring].\n\nFor users only interested in performance here are the benchmark results of this\npackage versus the others using Eric's Pi test (times are in ns/op sorted from\nfastest to slowest at 38 digits of precision):\n\n| digits | 9 | 19 | 38 | 100 | 500 | 5000 |\n|--------|--:|---:|---:|----:|----:|-----:|\n| Eric's decimal (Go) | 6415 | 30254 | 65171 | 194263 | 1731528 | 89841923 |\n| decimal | 12887 | 42720 | 100878 | 348865 | 4212811 | 342349031| \n| Eric's decimal (GDA) | 7124 | 39357 | 107720 | 392453 | 5421146 | 1175936547 |\n| Shopspring's decimal | 39528 | 96261 | 204017 | 561321 | 3402562 | 97370022 |\n| apd | 70833 | 301098 | 1262021 | 9859180 | 716558666 | ??? |\n\nNote that Eric's decimal uses a separate logic for decimals \u003c 1e19 (mantissa\nstored in a single uint64), which explains its impressive perfomance for low\nprecisions.\n\nIn additions and subtractions the operands' mantissae need to be aligned\n(shifted), this results in an additional multiplication by 10\\*\\*shift. In\nimplementations that use a binary representation of the matissa, this is faster\nfor shifts \u003c 19, but performance degrades as shifts get higher. With a decimal\nrepresentation, this requires a multiplication as well but always by a single\nWord, regardless of precision. \n\nRounding happens after every operation in decimal and Eric's decimal in GDA mode\n(not in Go mode, which explains its speed). Rounding requires a decimal shift\nright, which translates to a division by 10\\*\\*shift. Again for small shifts,\nbinary representations are faster, but degrades even faster as precision gets\nhigher. On decimal implementations, this operation is quite fast since it\ntranslates to a memcpy and a divmod of the least significant Word.\n\nThis explains why decimal's performace degrades slower than Eric's decimal-GDA\nas precision increases, and why Eric's decimal in Go mode is so fast (no\nrounding, which surprisingly counter-balances the high cost of mantissae\nalignment).\n\n## Caveats\n\nThe Float \u003c-\u003e Decimal conversion code needs some love.\n\nThe math/big API is designed to keep memory allocations to a minimum, but some\npeople find it cumbersome. Indeed it requires some practice to get used to it, \nso here's a quick rundown of what to do and not do:\n\nMost APIs look like:\n\n    func (z *Decimal) Add(x, y *Decimal) *Decimal\n\nwhere the function sets the receiver `z` to the result of `a + b` and returns\n'z'. The fact that the function returns the receiver is meant to allow chaining\nof operations:\n\n    s := new(Decimal).Mul(new(Decimal).Mul(r, r), pi) // d = r**2 * pi\n\nIf we don't care about what happens to `r`, we can just:\n\n    s := new(Decimal).Mul(r.Mul(r, r), pi)            // r *= r; d = r * pi\n\nand save one memory allocation.\n\nHowever, NEVER assign the result to a variable:\n\n    d := new(Decimal).SetUint(4)\n    d2 := d.Mul(d, d) // d2 == 16, but d == 16 as well!\n\nAgain, the sole intent behind returning the receiver is chaining of operations.\nBy assigning it to a variable, you will shoot yourself in the foot and kill\npuppies in some far away land, so never assign the result of an operation!\n\nHowever, feel free do do this:\n\n    d.Mul(d, d) // d = d*d\n\nThe code will properly detect that the receiver is also one of the arguments\n(possibly both), and allocate temporary storage space if (and only if)\nnecessary. Should this kind of construct fail, please file an issue.\n\n## License\n\nSimplified BSD license. See the [LICENSE] file.\n\nThe decimal package reuses a lot of code from the Go standard library, governed\nby a 3-Clause BSD license. See the [LICENSE-go] file.\n\nI'm aware that software using this package might have to include both licenses,\nwhich might be a hassle; tracking licenses from dependencies is hard enough as\nit is. I'd love to have a single license and hand over copyright to \"The Go\nauthors\", but the clause restricting use of the names of contributors for\nendorsement of a derived work in the 3-Clause BSD license that Go uses is\nproblematic. i.e. I can't just use it as-is, mentioning Google Inc., as that\nwould be an infringement in itself (well, that's the way I see it, but IANAL).\nOn the other hand, any piece of software written in Go should include the Go\nlicense anyway...\n\nAny helpful insights are welcome.\n\n[godoc]: https://pkg.go.dev/github.com/db47h/decimal?tab=doc\n[godocb]: https://img.shields.io/badge/go.dev-reference-blue\n[goreport]: https://goreportcard.com/report/github.com/db47h/decimal\n[goreportb]: https://goreportcard.com/badge/github.com/db47h/decimal\n[travis]: https://travis-ci.org/db47h/decimal\n[travisb]: https://travis-ci.org/db47h/decimal.svg?branch=master\n[cover]: https://coveralls.io/github/db47h/decimal?branch=master\n[coverb]: https://coveralls.io/repos/github/db47h/decimal/badge.svg?branch=master\n[eldecimal]: https://github.com/ericlagergren/decimal\n[apd]: https://github.com/cockroachdb/apd\n[Shopspring]: https://github.com/shopspring/decimal\n[LICENSE]: https://github.com/db47h/decimal/blob/master/LICENSE\n[LICENSE-go]: https://github.com/db47h/decimal/blob/master/LICENSE-go","funding_links":["https://patreon.com/db47h"],"categories":["Science and Data Analysis","Relational Databases","数据分析与数据科学","科学与数据分析"],"sub_categories":["HTTP Clients","查询语","HTTP客户端"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdb47h%2Fdecimal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdb47h%2Fdecimal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdb47h%2Fdecimal/lists"}