{"id":24092754,"url":"https://github.com/liarprincess/oh-my-decimal","last_synced_at":"2026-05-16T03:02:45.375Z","repository":{"id":203797033,"uuid":"708921432","full_name":"LiarPrincess/Oh-my-decimal","owner":"LiarPrincess","description":"IEEE 754 decimal","archived":false,"fork":false,"pushed_at":"2024-01-25T08:02:40.000Z","size":5170,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"mr-darcy","last_synced_at":"2025-01-10T08:40:35.811Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LiarPrincess.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-23T16:46:21.000Z","updated_at":"2024-01-25T07:58:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"69caf6ad-3c6b-4da0-b37d-12fa3942e16a","html_url":"https://github.com/LiarPrincess/Oh-my-decimal","commit_stats":null,"previous_names":["liarprincess/oh-my-decimal"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiarPrincess%2FOh-my-decimal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiarPrincess%2FOh-my-decimal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiarPrincess%2FOh-my-decimal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiarPrincess%2FOh-my-decimal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiarPrincess","download_url":"https://codeload.github.com/LiarPrincess/Oh-my-decimal/tar.gz/refs/heads/mr-darcy","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240995346,"owners_count":19890717,"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":[],"created_at":"2025-01-10T08:37:34.921Z","updated_at":"2026-05-16T03:02:45.291Z","avatar_url":"https://github.com/LiarPrincess.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"Oh-my-decimal is a weird Frankenstein's monster that combines:\n- decimal from IEEE 754 2008\n- `Swift.Double`\n\nIf `Swift.Double` overrides the standard then `Swift.Double` behavior is implemented with the following exceptions:\n\n- sign of `sNaN` follows the same rules as sign of `qNaN` - this is not the case for `Swift.Double`. Note that while the creation of `sNaN` (`copy`, `copySign`, `scaleB` etc.) will give `sNaN`, most of the arithmetic operations will still return `qNaN` with `invalidOperation` flag raised.\n\n- value returned by the `significand` property is always positive. In Swift `(-Double.nan).significand` will return `-nan`. This is needed to make the `scaleB` axiom work: `let y = F(sign: x.sign, exponent: x.exponent, significand: x.significand)` then `x` and `y` [should be equal](https://developer.apple.com/documentation/swift/floatingpoint/init(sign:exponent:significand:)). Obviously `NaNs` are never equal (I'm not sure why documentation is written in this way), but we will have the same sign, signaling bit and payload. Note that:\n  - `oh-my-decimal` does not implement `FloatingPoint` protocol from which this requirement comes from.\n  - both `oh-my-decimal` and `Swift.Double` will return `sNaN` if the `significand` argument of `scaleB` is `sNaN`. Standard would return `qNaN` and raise `invalidOperation`.\n\n- `minimum/maximum`\n  - `oh-my-decimal` implements the standard 2008: if one of the operands is `sNaN` then the result is a `NaN` with `invalidOperation` raised.\n  - standard 2019 introduces new operations as there was [a whole debate](https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf) about the corner cases of 2008.\n  - [Swift documentation](https://developer.apple.com/documentation/swift/double/minimum(_:_:)) says: \"If both x and y are NaN, or either x or y is a signaling NaN, the result is NaN\", with a link to the standard 2008. In practice for `sNaN` it returns the non-NaN operand.\n\n- no `.awayFromZero` rounding - this is trivial to implement, but only [speleotrove](https://speleotrove.com/decimal/) contains tests for it (they call it `round-up`). Since rounding is present in most of the operations, a single test suite is not enough to be fully sure that everything works correctly. In `oh-my-decimal` most important things are covered by: [Intel](https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html), [Speleotrove](https://speleotrove.com/decimal/), [Hossam A. H. Fahmy](http://eece.cu.edu.eg/~hfahmy/arith_debug/) and [oh-my-decimal-tests](https://github.com/LiarPrincess/Oh-my-decimal-tests) tests. Also, IEEE 754 does not require this rounding mode, so 🤷.\n\n- missing protocols:\n  - `FloatingPoint` - we have our own `DecimalFloatingPoint`.\n  - `ExpressibleByFloatLiteral` - Swift converts to `Float80`/`Double` and then converts to a number. This conversion may not be exact, so it is basically a random number generator.\n  - `Strideable` - really quickly it would break the Sterbenz lemma: `y/2 \u003c x \u003c 2y`. What is the distance between `greatestFiniteMagnitude` and `leastNormalMagnitude`?\n  - `Random` - apart from a few specific input ranges it would not do what user wants:\n    - simple random between 0 and 10 would be skewed towards smaller numbers\n      because more of them are representable (tons of possible negative exponents).\n    - if we generated truly random (infinitely precise) value and rounded then\n      bigger numbers would be more common (they have bigger ulp).\n\nExamples (Intel, this library was not tested on Apple silicon):\n\n```Swift\n// Container for IEEE 754 flags: inexact, invalidOperation etc.\nvar status = DecimalStatus()\n\n// Standard: nan + invalidOperation\n// Swift: nan\nprint(Decimal64.signalingNaN.nextUp(status: \u0026status)) // nan + invalidOperation 🟢\nprint(Double.signalingNaN.nextUp) // nan 🟢\nstatus.clearAll()\n\n// Standard: nan + invalidOperation\n// Swift: nan\nprint(Decimal64.signalingNaN + Decimal64.signalingNaN) // nan 🟢\nprint(Decimal64.signalingNaN.adding(Decimal64.signalingNaN, rounding: .towardZero, status: \u0026status)) // nan + invalidOperation 🟢\nprint(Double.signalingNaN + Double.signalingNaN) // nan 🟢\nstatus.clearAll()\n\n// Standard: nan + invalidOperation\n// Swift: https://www.youtube.com/watch?v=nptj1uWFy5s\nprint((-Decimal64.signalingNaN).magnitude) // snan 🔴\nprint((-Double.signalingNaN).magnitude) // snan 🔴\n\n// 'scaleB' axiom\nlet d1 = -Decimal64.nan\nprint(Decimal64(sign: d1.sign, exponent: 0, significand: d1.significand)) // -nan 🟢\nlet d2 = -Double.nan\nprint(Double(sign: d2.sign, exponent: 0, significand: d2.significand)) // nan 🔴\n\n// Standard: canonicalized number\n// Swift: number\nprint(Decimal64.minimum(Decimal64.nan, 1, status: \u0026status)) // 1E+0 🟢\nprint(Double.minimum(Double.nan, 1)) // 1.0 🟢\n\n// Standard: nan + invalidOperation\n// Swift: number\nprint(Decimal64.minimum(1, Decimal64.signalingNaN, status: \u0026status)) // nan + invalidOperation 🟢\nprint(Double.minimum(1, Double.signalingNaN)) // 1.0 🔴\n```\n\n## Branches\n- `mr-darcy` (this branch) - Swift implementation.\n- `mr-bingley` - wrapper for [Intel library](https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html). It has `pow`, but `DecimalStatus` is not publicly available.\n\n## Code\n\n**`Sources/Decimal`**\n\n- `Generated` - code generated by Python scripts.\n  - `Decimal32`\n  - `Decimal64`\n  - `Decimal128`\n  - `DecimalFloatingPoint` - dem protocol.\n\n- `DecimalMixin` - internal protocol on which every operation is defined. All of the methods from `DecimalXX` types will eventually call a method from `DecimalMixin`. Methods start with '`_`' to avoid name clashes with the `public` methods exported from `DecimalXX` types.\n\n- `DecimalFloatingPointRoundingRule` - similar to `FloatingPointRoundingRule` but without `awayFromZero` - not required by IEEE 754, not enough test cases to guarantee correctness.\n\n- `DecimalStatus` - holds IEEE 754 flags: `isInvalidOperation`, `isDivisionByZero`, `isOverflow`, `isUnderflow`, and `isInexact`. Lightweight, you can create as many statuses as you want, they are completely independent. Usually the last argument:\n\n  ```Swift\n  public func adding(\n    _ other: Self,\n    rounding: DecimalFloatingPointRoundingRule,\n    status: inout DecimalStatus\n  ) -\u003e Self { … }\n  ```\n\n**`Sources/test-hossam-fahmy`** - app to run `Hossam-Fahmy-tests` and `Oh-my-decimal-tests`. Use `make test-hossam-fahmy` to run in RELEASE mode. It finishes in ~10min on Intel Pentium G4560. Probably faster if you have better CPU, this thing eats CPU cores like candies.\n\n**`Tests/DecimalTests`**\n- `Generated` - unit tests generated by Python scripts.\n- `Intel - generated` - unit tests generated from Intel test suite (`Test-suites/IntelRDFPMathLib20U2`).\n- `Speleotrove - generated` - unit tests generated from [Speleotrove](https://speleotrove.com/decimal/) test suite (`Test-suites/speleotrove-dectest`).\n\n**`Test-suites`**\n\n- `IntelRDFPMathLib20U2` - put [Intel decimal](https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html) here. Or not. This is only used for generating unit tests. Use `make gen` to re-generate and `make test` to run.\n\n- `speleotrove-dectest` - put [Speleotrove](https://speleotrove.com/decimal/) test suite here.  Or not. This is only used for generating unit tests. Use `make gen` to re-generate and `make test` to run.\n\n- `Hossam-Fahmy-tests` - put [Hossam A. H. Fahmy test suite](http://eece.cu.edu.eg/~hfahmy/arith_debug/) here. Use `make test-hossam-fahmy` to run.\n\n- `Oh-my-decimal-tests` - put [oh-my-decimal-test-suite](https://github.com/LiarPrincess/Oh-my-decimal-tests) here. Use `make test-hossam-fahmy` to run.\n\n**`Scripts`** - Python code generators. Use `make gen` to run.\n\n## Makefile\n\n- `make build` - …?\n- `make test` - run unit tests.\n- `make test-hossam-fahmy` - run `Hossam-Fahmy-tests` and `Oh-my-decimal-tests` tests.\n- `make x` - run a subset of unit tests. Remember to modify the `Makefile` to re-define what this subset is.\n- `make run` - run `Experiments.test_main` unit test. This is the “playground” used for ad-hoc tests when writing the library.\n- `make gen` - run Python scripts to generate code.\n- `make intel-copy` - create a directory with links to the most important Intel files.\n\nMost of the time the workflow is: `make x`, `make x`, `make x`, `make test`, and finally `make test-hossam-fahmy`.\n\n## Missing from IEEE 754\n\n- HexCharacter - `Swift.Double` actually has this, but it is not widely used and I am not into writing parsers.\n  - convertFrom\n  - convertTo\n- compareQuiet\n  - Ordered\n  - Unordered\n- compareSignaling - tiny modification of `compareQuiet` that can be added in `extension` if user so desires.\n  - Equal\n  - NotEqual\n  - Greater\n  - GreaterEqual\n  - GreaterUnordered\n  - NotGreater\n  - Less\n  - LessEqual\n  - LessUnordered\n  - NotLess\n\nSide note: for compare operations you want to read IEEE 754 2019 instead of 2008. The content is the same, but the language is more approachable.\n\n## Differences between IEEE 754 and `oh-my-decimal`\n\n|          | IEEE 754 | Oh-my-decimal |\n|----------|----------|---------------|\n| Unary `+`\u003cbr/\u003eUnary `-`\u003cbr/\u003e`magnitude`\u003cbr/\u003e`copy`\u003cbr/\u003e`copySign`\u003cbr/\u003e`init(sign:exponent:significand:rounding:status:)` (scaleB) | `sNaN` returns `NaN` and raises `invalidOperation`.      | `sNaN` returns `sNaN`, no flags raised.        |\n\nMaybe something else, but in general it follows `Swift.Double`, so you know what to expect.\n\n## Do NOT use\n\n| Operation | Reason |\n|-----------|--------|\n| Cute operators like `*` or `/`\u003cbr/\u003e(maybe even `+` or `-`) | Use the overloads with the `rounding` argument. Bonus points for using `status`. |\n|`addingProduct`\u003cbr/\u003e(fused multiply add, FMA)| Most of the time you actually want the intermediate rounding.|\n|Binary floating point interop| Bullies from IEEE forced us to implement this (`formatOf-convertFormat(source)` operation). **[NEVER EVER](https://www.youtube.com/watch?v=WA4iX5D9Z64) USE THIS THINGIE. MASSIVE 🚩 WHEN YOU SEE SOMEBODY DOING THIS.**|\n|`Decimal128._UInt128`| This is not a general purpose `UInt128`. It works for `Decimal`, but it may not work in your specific case. No guarantees.|\n\n## Renames\n\n- `round(decimalDigitCount:)` = `quantized`\n  ```Swift\n  let d = Decimal128(\"123.456789\")!\n  let precision = Decimal128(\"0.01\")!\n  var status = DecimalStatus()\n  let result = d.quantized(to: precision, rounding: .towardZero, status: \u0026status)\n  print(result, status) // 12345E-2, isInexact\n  status.clear(.isInexact)\n\n  // Inexact flag will not be raised if the result is… well… exact.\n  let d2 = Decimal128(\"123.450000\")!\n  let result2 = d2.quantized(to: precision, rounding: .towardZero, status: \u0026status)\n  print(result2, status) // 12345E-2, empty\n\n  // But remember that you can't store more digits than supported by a given format.\n  // Doing so will result in 'nan' with 'InvalidOperation' raised.\n  // For example 'Decimal32' can store only 7 significand digits:\n  let d32 = Decimal32(\"1234567\")!\n  let precision32 = Decimal32(\"0.1\")!\n  let result32 = d32.quantized(to: precision32, rounding: .towardZero, status: \u0026status)\n  print(result32, status) // nan, isInvalidOperation\n  ```\n\n- multiply by power of 10 = `init(sign:exponent:significand:rounding:status:)` (also known as `scaleB`)\n\n  ```Swift\n  let d = Decimal64(\"1234\")!\n  var status = DecimalStatus()\n  let result = Decimal64(sign: .plus, exponent: 20, significand: d, rounding: .towardZero, status: \u0026status)\n  print(d) // 1234E+0\n  print(result, status) // 1234E+20, DecimalStatus()\n  ```\n\n## Contributions etc.\n\nOh-my-decimal is feature complete, no new functionalities are planned. At some point I may add `pow` with `Int` argument, but probably not…\n\nDo not submit any of the following PRs - they will NOT be merged:\n- `pow` with `Int` argument - I want to write this myself.\n- PeRfOrMaNcE - especially any of the `@inlinable/usableFromInline` things. Just don't.\n\n## Code style\n\n- 2-space indents and no tabs at all\n- 80 characters per line\n    - You will get a [SwiftLint](https://github.com/realm/SwiftLint) warning if you go over 100.\n    - Over 120 will result in a compilation error.\n    - If 80 doesn't give you enough room to code, your code is too complicated - consider using subroutines (advice from [PEP-7](https://www.python.org/dev/peps/pep-0007/)).\n- Required `self` in methods and computed properties\n    - All of the other method arguments are named, so we will require it for this one.\n    - `Self`/`type name` for static methods is recommended, but not required.\n    - I’m sure that they will depreciate the implicit `self` in the next major Swift version 🤞. All of that source breakage is completely justified.\n- No whitespace at the end of the line\n    - Some editors may remove it as a matter of routine and we don’t want weird git diffs.\n- (pet peeve) Try to introduce a named variable for every `if` condition.\n    - You can use a single logical operator - something like `if !isPrincess` or `if isDisnepCharacter \u0026\u0026 isPrincess` is allowed.\n    - Do not use `\u0026\u0026` and `||` in the same expression, create a variable for one of them.\n    - If you need parens then it is already too complicated.\n\n## License\n\nOh-my-decimal is distributed under the “GNU General Public License”. You are NOT permitted to copy the code and distribute it solely under MIT.\n\n`Tests/DecimalTests/Intel - generated` is generated from [Intel code](https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html). This makes it dual-licensed. Intel license is available in `LICENSE-Intel` file.\n\n`Tests/DecimalTests/Speleotrove - generated` is generated from [Speleotrove test suite](https://speleotrove.com/decimal/). This makes it dual-licensed. Speleotrove license is available in `LICENSE-speleotrove-dectest` file.\n\n`Hossam-Fahmy-tests` are not a part of this repository, but just for completeness their license is available in `LICENSE-Hossam-Fahmy-tests` file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliarprincess%2Foh-my-decimal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliarprincess%2Foh-my-decimal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliarprincess%2Foh-my-decimal/lists"}