From d1a390af47cdcddcfad9cea080c24bcb2635ae10 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Wed, 22 Oct 2025 16:16:36 +0200 Subject: [PATCH 1/9] Remove error handling section --- docs/cookbook.md | 49 ------------------------------------------------ 1 file changed, 49 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index 8171b5d0..dce32449 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -469,52 +469,3 @@ const prices = ["19.99", "5.50", "105.00", "0.99", "50.00"]; console.log(findMinMax(prices)); // => { min: "0.99", max: "105.00" } ``` - -## Error Handling - -### Safe parsing of user input - -Handle potentially invalid decimal input: - -```javascript -function parseUserDecimal(input) { - try { - const decimal = new Decimal(input); - - // Check for special values - if (decimal.isNaN()) { - return { success: false, error: "Invalid number" }; - } - if (!decimal.isFinite()) { - return { success: false, error: "Number must be finite" }; - } - - return { success: true, value: decimal }; - } catch (error) { - return { success: false, error: "Invalid decimal format" }; - } -} -``` - -### Division by zero handling - -Safely handle division operations: - -```javascript -function safeDivide(dividend, divisor) { - const a = new Decimal(dividend); - const b = new Decimal(divisor); - - if (b.equals(new Decimal(0))) { - return { success: false, error: "Division by zero" }; - } - - const result = a.divide(b); - - if (!result.isFinite()) { - return { success: false, error: "Result is infinite" }; - } - - return { success: true, value: result }; -} -``` From a1df094c25fbb3077129a73a0d09c9654599b535 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Wed, 22 Oct 2025 16:01:38 +0200 Subject: [PATCH 2/9] Fix round() API to use string parameter, not options object The spec defines round() as: round(numFractionalDigits, roundingMode) where roundingMode is a string, not an options object. Changed all examples from: value.round(2, { roundingMode: "floor" }) to: value.round(2, "floor") This matches both the spec and the polyfill implementation. --- docs/cookbook.md | 14 +++++++------- docs/decimal.md | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index dce32449..d0561835 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -67,7 +67,7 @@ function splitBill(totalAmount, numberOfPeople) { const perPerson = total.divide(people); // Round down to cents for most people - const roundedPerPerson = perPerson.round(2, { roundingMode: "floor" }); + const roundedPerPerson = perPerson.round(2, "floor"); // Last person pays the remainder to ensure exact total const lastPersonPays = total.subtract( @@ -361,21 +361,21 @@ const value = new Decimal("123.456"); console.log(value.round(2).toString()); // => "123.46" // Always round up (ceiling) -console.log(value.round(2, { roundingMode: "ceil" }).toString()); // => "123.46" +console.log(value.round(2, "ceil").toString()); // => "123.46" // Always round down (floor) -console.log(value.round(2, { roundingMode: "floor" }).toString()); // => "123.45" +console.log(value.round(2, "floor").toString()); // => "123.45" // Round towards zero (truncate) -console.log(value.round(2, { roundingMode: "trunc" }).toString()); // => "123.45" +console.log(value.round(2, "trunc").toString()); // => "123.45" // Round half away from zero -console.log(value.round(2, { roundingMode: "halfExpand" }).toString()); // => "123.46" +console.log(value.round(2, "halfExpand").toString()); // => "123.46" // Negative number example const negative = new Decimal("-123.456"); -console.log(negative.round(2, { roundingMode: "floor" }).toString()); // => "-123.46" -console.log(negative.round(2, { roundingMode: "ceil" }).toString()); // => "-123.45" +console.log(negative.round(2, "floor").toString()); // => "-123.46" +console.log(negative.round(2, "ceil").toString()); // => "-123.45" ``` ### Financial rounding diff --git a/docs/decimal.md b/docs/decimal.md index 7dc4a30c..55223025 100644 --- a/docs/decimal.md +++ b/docs/decimal.md @@ -195,20 +195,20 @@ new Decimal("-123.45").negate().toString(); // => "123.45" All rounding methods follow IEEE 754-2019 rounding modes. -### **round**(_scale_?: number, _options_?: { roundingMode?: string }) : Decimal +### **round**(_scale_?: number, _roundingMode_?: string) : Decimal Rounds to a given number of decimal places. **Parameters:** - `scale` (number): Number of decimal places to round to (default: 0) -- `options.roundingMode` (string): One of "ceil", "floor", "trunc", "halfEven" (default), or "halfExpand" +- `roundingMode` (string): One of "ceil", "floor", "trunc", "halfEven" (default), or "halfExpand" ```js const d = new Decimal("123.456"); d.round().toString(); // => "123" d.round(2).toString(); // => "123.46" -d.round(2, { roundingMode: "floor" }).toString(); // => "123.45" +d.round(2, "floor").toString(); // => "123.45" ``` ## Comparison Methods From 2bdda1d3cc9308a08d5f1a95abbec18e451264b3 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 11:39:55 +0200 Subject: [PATCH 3/9] Add Prior Art section, strengthen motivation arguments Address concerns about the Decimal proposal's motivation and adoption potential by adding a Prior Art sections. --- README.md | 46 +++++++++++++- docs/cookbook.md | 153 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 192 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 32496113..716f51d2 100644 --- a/README.md +++ b/README.md @@ -201,11 +201,55 @@ If Decimal becomes a part of standard JavaScript, it may be used in some built-i More host API interactions are discussed in [#5](https://github.com/tc39/proposal-decimal/issues/5). +## Prior Art + +Adding decimal arithmetic would not be an instance of JS breaking new ground. Many major programming languages have recognized that decimal arithmetic is fundamental enough to include in the standard library, if not as a primitive type. + +### Decimal Support Across Languages + +| Language | Type Name | Location | Year Added | Notes | +|----------|-----------|----------|------------|-------| +| Python | `decimal.Decimal` | Standard library | 2003 (Python 2.3) | Based on [General Decimal Arithmetic Specification](http://speleotrove.com/decimal/) | +| Java | `java.math.BigDecimal` | Standard library | 1998 (JDK 1.1) | Arbitrary precision, widely used for financial calculations | +| C# | `decimal` | Primitive type | 2000 (C# 1.0) | 128-bit decimal type, first-class language support | +| Swift | `Decimal` (formerly `NSDecimalNumber`) | Foundation framework | 2016 (Swift 3.0) | Standard library type for financial calculations | +| Ruby | `BigDecimal` | Standard library | 1999 (Ruby 1.6) | Arbitrary precision decimal arithmetic | + +SQL also has `NUMERIC`/`DECIMAL` as a built-in type, predating many programming languages. + +Many languages added decimal support 10+ years ago. The IEEE 754-2008 standard for Decimal128 is now 17 years old. (The 2019 edition of IEEE 754 also has decimal arithmetic.) + Indeed, multiple numeric types are not unusual. Languages ship with integers, floats, *and* decimals. Python even includes both decimals and rationals. The existence of JS's `Number` and `BigInt` doesn't preclude `Decimal`. Moreover, the need for decimal types across many languages reflects a genuine, universal need rather than a niche requirement. + +### Why JavaScript Lacks Decimal Support + +JavaScript's origins as a lightweight browser scripting language meant it initially shipped with minimal numeric support (just IEEE 754 binary floats). However, JavaScript's role has fundamentally changed: + +- **Then (1995)**: Simple form validation and DOM manipulation +- **Now (2025)**: Full-stack applications, financial systems, e-commerce platforms, serverless backends, data processing pipelines + +The language has evolved to meet modern needs (adding `BigInt`, `async`/`await`, modules, etc.), but decimal arithmetic remains a critical gap. + +### The Cost of Not Having Decimal + +Because JavaScript lacks native decimal support, developers have created numerous userland solutions: + +- [**decimal.js**](https://www.npmjs.com/package/decimal.js): ~2M weekly npm downloads +- [**bignumber.js**](https://www.npmjs.com/package/bignumber.js): ~800K weekly npm downloads +- [**big.js**](https://www.npmjs.com/package/big.js): ~500K weekly npm downloads + +There are also money-specific libraries such as [dinero.js](https://www.npmjs.com/package/dinero.js) with about 180K weekly NPM downloads. + +Each implementation has slightly different semantics, requires coordination across libraries, adds to total bundle size, presumably performs worse than native implementations could, and creates interoperability challenges. + +Compare this to BigInt: before native support, polyfills and userland big integer libraries had minimal adoption because the *need* was niche. Decimal libraries show massive adoption because the need is universal. + +Thus, Decimal standardizes existing practice. Developers are already using decimal libraries (millions of downloads/week), converting numbers to "cents" (error-prone, confusing), using `Number` incorrectly (causing bugs). Decimal doesn't ask developers to adopt something new; it offers a better way to do what they're already struggling to do. + ## Specification and standards Based on feedback from JS developers, engine implementors, and the members of the TC39 committee, we have a concrete proposal. Please see the [spec text](https://github.com/tc39/proposal-decimal/blob/main/spec.emu) ([HTML version](https://github.com/tc39/proposal-decimal/blob/main/spec.emu)). are provided below. You’re encouraged to join the discussion by commenting on the issues linked below or [filing your own](https://github.com/tc39/proposal-decimal/issues/new). -We will use the **Decimal128** data model for JavaScript decimals. Decimal128 is not a new standard; it was added to the IEEE 754 floating-point arithmetic standard in 2008. It represents the culmination of decades of research, both theoretical and practical, on decimal floating-point numbers. Values in the Decimal128 universe take up 128 bits. In this representation, up to 34 significant digits (that is, decimal digits) can be stored, with an exponent (power of ten) of +/- 6143. +We will use the **Decimal128** data model for JavaScript decimals. Decimal128 is not a new standard; it was added to the IEEE 754 floating-point arithmetic standard in 2008 (and is present in the 2019 edition of IEEE 754, which JS normatively depends upon). It represents the culmination of decades of research on decimal floating-point numbers. Values in the Decimal128 universe take up 128 bits. In this representation, up to 34 significant digits (that is, decimal digits) can be stored, with an exponent (power of ten) of +/- 6143. In addition to proposing a new `Decimal` class, we propose a `Decimal.Amount` class for storing a Decimal number together with a precision. The `Decimal.Amount` class is important mainly for string formatting purposes, where one desires to have a notion of a number that “knows” how precise it is. We do not intend to support arithmetic on `Decimal.Amount` values, the thinking being that `Decimal` already supports arithmetic. diff --git a/docs/cookbook.md b/docs/cookbook.md index d0561835..b484ba4c 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -20,16 +20,150 @@ const total = price.multiply(quantity); console.log(total.toString()); // => "59.97" ``` +## Prior Art: Decimal Arithmetic in Other Languages + +The patterns demonstrated in this cookbook are not unique to JavaScript - they reflect well-established practices across programming languages. This section shows how other languages handle the same decimal arithmetic challenges, demonstrating that native decimal support is standard across the industry. + +### Python + +Python includes the `decimal` module in its standard library (since 2003): + +```python +from decimal import Decimal + +# Financial calculations +price = Decimal("19.99") +quantity = Decimal("3") +tax_rate = Decimal("0.0825") + +subtotal = price * quantity +tax = subtotal * tax_rate +total = subtotal + tax + +print(f"${total:.2f}") # => "$64.62" +``` + +Python's `Decimal` is widely used in web frameworks like Django and Flask for handling monetary values. The Django ORM includes a `DecimalField` specifically for financial data. + +### Java + +Java's `BigDecimal` (in the standard library since 1998) is ubiquitous in enterprise applications: + +```java +import java.math.BigDecimal; +import java.math.RoundingMode; + +BigDecimal price = new BigDecimal("19.99"); +BigDecimal quantity = new BigDecimal("3"); +BigDecimal taxRate = new BigDecimal("0.0825"); + +BigDecimal subtotal = price.multiply(quantity); +BigDecimal tax = subtotal.multiply(taxRate); +BigDecimal total = subtotal.add(tax); + +// Round to 2 decimal places +total = total.setScale(2, RoundingMode.HALF_UP); +System.out.println(total); // => "64.62" +``` + +`BigDecimal` is the standard approach for financial applications in the Java ecosystem, including frameworks like Spring and Hibernate. + +### C\# + +C# includes `decimal` as a primitive type (since 2000), giving it first-class language support: + +```csharp +decimal price = 19.99m; +decimal quantity = 3m; +decimal taxRate = 0.0825m; + +decimal subtotal = price * quantity; +decimal tax = subtotal * taxRate; +decimal total = subtotal + tax; + +Console.WriteLine($"${total:F2}"); // => "$64.92" +``` + +### Ruby + +Ruby includes `BigDecimal` in its standard library: + +```ruby +require 'bigdecimal' + +price = BigDecimal("19.99") +quantity = BigDecimal("3") +tax_rate = BigDecimal("0.0825") + +subtotal = price * quantity +tax = subtotal * tax_rate +total = subtotal + tax + +puts "$%.2f" % total # => "$64.92" +``` + +Ruby on Rails uses [`BigDecimal`](https://ruby-doc.org/3.4.1/exts/json/BigDecimal.html) for handling database `decimal` columns by default, making it the standard approach for money in Rails applications. + +### Swift + +Swift's Foundation framework includes [`Decimal`](https://developer.apple.com/documentation/foundation/decimal) (formerly [`NSDecimalNumber`](https://developer.apple.com/documentation/foundation/nsdecimalnumber)): + +```swift +import Foundation + +let price = Decimal(string: "19.99")! +let quantity = Decimal(string: "3")! +let taxRate = Decimal(string: "0.0825")! + +let subtotal = price * quantity +let tax = subtotal * taxRate +let total = subtotal + tax + +let formatter = NumberFormatter() +formatter.numberStyle = .currency +print(formatter.string(from: total as NSDecimalNumber)!) // => "$64.62" +``` + +Swift's `Decimal` is the recommended type for financial calculations in iOS and macOS applications. + +### SQL + +Most SQL databases treat decimal arithmetic as fundamental: + +```sql +SELECT + price, + quantity, + price * quantity AS subtotal, + (price * quantity) * 0.0825 AS tax, + (price * quantity) * 1.0825 AS total +FROM products +WHERE id = 1; +``` + +Database `NUMERIC` and `DECIMAL` types provide exact decimal arithmetic. When JavaScript applications query these values, they currently must receive them as strings (or as `Number`s, which may lose precision from the get-go) that are then converted to `Number` (which also loses precision), or use a userland decimal library. + +Native JavaScript Decimal would allow seamless interchange with database decimal types. + +### Why This Matters for JavaScript + +JavaScript forces developers to choose between: + +- Binary floats (precision errors, bugs, non-trivial knowledge of binary float problems and some countermeasures that may not always work) +- Userland libraries (bundle size, coordination, performance) +- Integer "cents" (cognitive overhead, internationalization issues) + +The patterns in this cookbook reflect industry-standard practices that JavaScript developers should be able to use natively, just as developers in other major languages can. + ## Common Patterns This section demonstrates common patterns and best practices when working with Decimal. ### Working with Monetary Values -Always create Decimal values from strings to ensure exact representation: +Create Decimal values from strings to ensure exact representation: ```javascript -// Good: Create from string const price = new Decimal("19.99"); const tax = new Decimal("0.0825"); @@ -41,7 +175,7 @@ console.log(total.toFixed(2)); // => "21.64" ### Accumulating Values -When accumulating many values over the course of a calculation with several steps, use Decimal to avoid rounding errors: +When accumulating many values in the course of a calculation with several steps, use Decimal to avoid rounding errors: ```javascript const transactions = ["10.50", "25.75", "3.99", "100.00", "45.25"]; @@ -111,7 +245,7 @@ console.log(tip); // => { amount: "6.83", total: "52.33" } ### Creating Decimal values -The most reliable way to create exact decimal values is from strings: +The most reliable way to create Decimal values is from strings: ```javascript // From string (recommended for exact values) @@ -171,7 +305,14 @@ try { } ``` -Very large integers beyond Number's safe range work perfectly: +If one wants to ensure that a Decimal value represents, semantically, an integer, use `round` before convering to BigInt: + +```javascript +const fractionalDecimal = new Decimal("123.45"); // same as previous example +fractionalDecimal.round().toBigInt(); // ...but doesn't throw +``` + +Very large integers beyond `Number`'s safe range work perfectly: ```javascript const largeDecimal = new Decimal("99999999999999999999999999999999"); @@ -179,7 +320,7 @@ const largeBigInt = largeDecimal.toBigInt(); console.log(largeBigInt); // => 99999999999999999999999999999999n ``` -(However, there are some digit strings that Number can handle just fine, namely those with more than 34 significant digits that have compact representations as sums of powers of two.) +However, there are some digit strings that Number can handle just fine, namely those with more than 34 significant digits that have compact representations as sums of powers of two. ## Financial Calculations From 4d114ac23b3437c8aed0b5d4dd5d58b0104d2ac7 Mon Sep 17 00:00:00 2001 From: jessealama Date: Thu, 23 Oct 2025 12:27:56 +0000 Subject: [PATCH 4/9] fixup: [spec] `npm run build` --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index d44dbb4b..bbdc3da3 100644 --- a/index.html +++ b/index.html @@ -3231,7 +3231,7 @@

Stage 1 Draft / October 22, 2025

Decimal

+

Stage 1 Draft / October 23, 2025

Decimal

Introduction

From 5029b2c9f2a54cbb5e9dc6a20d1b5ad12de4c677 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 15:04:47 +0200 Subject: [PATCH 5/9] Fix arithmetic --- docs/cookbook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index b484ba4c..16ecc7b2 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -40,7 +40,7 @@ subtotal = price * quantity tax = subtotal * tax_rate total = subtotal + tax -print(f"${total:.2f}") # => "$64.62" +print(f"${total:.2f}") # => "$64.92" ``` Python's `Decimal` is widely used in web frameworks like Django and Flask for handling monetary values. The Django ORM includes a `DecimalField` specifically for financial data. @@ -121,7 +121,7 @@ let total = subtotal + tax let formatter = NumberFormatter() formatter.numberStyle = .currency -print(formatter.string(from: total as NSDecimalNumber)!) // => "$64.62" +print(formatter.string(from: total as NSDecimalNumber)!) // => "$64.92" ``` Swift's `Decimal` is the recommended type for financial calculations in iOS and macOS applications. From a42dc5b2dc035b1dacf01be71b35acefa85ff106 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 15:07:14 +0200 Subject: [PATCH 6/9] Add links --- docs/cookbook.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index 16ecc7b2..7a298680 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -47,7 +47,7 @@ Python's `Decimal` is widely used in web frameworks like Django and Flask for ha ### Java -Java's `BigDecimal` (in the standard library since 1998) is ubiquitous in enterprise applications: +Java's [`BigDecimal`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html) (in the standard library since 1998) is ubiquitous in enterprise applications: ```java import java.math.BigDecimal; @@ -70,7 +70,7 @@ System.out.println(total); // => "64.62" ### C\# -C# includes `decimal` as a primitive type (since 2000), giving it first-class language support: +C# includes [`decimal`](https://learn.microsoft.com/en-us/dotnet/api/system.decimal?view=net-9.0) as a primitive type (since 2000), giving it first-class language support: ```csharp decimal price = 19.99m; @@ -128,7 +128,7 @@ Swift's `Decimal` is the recommended type for financial calculations in iOS and ### SQL -Most SQL databases treat decimal arithmetic as fundamental: +Most SQL databases (e.g., [PostgreSQL](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL)) treat decimal arithmetic as fundamental: ```sql SELECT From 59bcd945fa39c4020362eb862359088602be1efc Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 15:12:28 +0200 Subject: [PATCH 7/9] Add more links --- docs/cookbook.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index 7a298680..69ffe41a 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -26,7 +26,7 @@ The patterns demonstrated in this cookbook are not unique to JavaScript - they r ### Python -Python includes the `decimal` module in its standard library (since 2003): +Python includes the [`decimal`](https://docs.python.org/3/library/decimal.html) module in its standard library (since 2003): ```python from decimal import Decimal @@ -43,7 +43,7 @@ total = subtotal + tax print(f"${total:.2f}") # => "$64.92" ``` -Python's `Decimal` is widely used in web frameworks like Django and Flask for handling monetary values. The Django ORM includes a `DecimalField` specifically for financial data. +Python's `Decimal` is widely used in web frameworks like [Django](https://www.djangoproject.com) and [Flask](https://flask.palletsprojects.com/en/stable/) for handling monetary values. Django includes a [`DecimalField`](https://docs.djangoproject.com/en/5.2/ref/models/fields/#decimalfield) specifically for such data. ### Java @@ -66,7 +66,7 @@ total = total.setScale(2, RoundingMode.HALF_UP); System.out.println(total); // => "64.62" ``` -`BigDecimal` is the standard approach for financial applications in the Java ecosystem, including frameworks like Spring and Hibernate. +`BigDecimal` is the standard approach for financial applications in the Java ecosystem, including frameworks like [Spring](https://spring.io/projects/spring-framework) and [Hibernate](https://hibernate.org). ### C\# @@ -86,7 +86,7 @@ Console.WriteLine($"${total:F2}"); // => "$64.92" ### Ruby -Ruby includes `BigDecimal` in its standard library: +Ruby includes [`BigDecimal`]((https://ruby-doc.org/3.4.1/exts/json/BigDecimal.html)) in its standard library: ```ruby require 'bigdecimal' @@ -102,7 +102,7 @@ total = subtotal + tax puts "$%.2f" % total # => "$64.92" ``` -Ruby on Rails uses [`BigDecimal`](https://ruby-doc.org/3.4.1/exts/json/BigDecimal.html) for handling database `decimal` columns by default, making it the standard approach for money in Rails applications. +Ruby on Rails uses `BigDecimal` for handling database `decimal` columns by default, making it the standard approach for money in Rails applications. ### Swift From 1dd317e3799732e9c83067dfbd383309f78dc6a3 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 15:13:52 +0200 Subject: [PATCH 8/9] Qualify the need for precision --- docs/cookbook.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index 69ffe41a..c4dd564a 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -143,11 +143,9 @@ WHERE id = 1; Database `NUMERIC` and `DECIMAL` types provide exact decimal arithmetic. When JavaScript applications query these values, they currently must receive them as strings (or as `Number`s, which may lose precision from the get-go) that are then converted to `Number` (which also loses precision), or use a userland decimal library. -Native JavaScript Decimal would allow seamless interchange with database decimal types. - ### Why This Matters for JavaScript -JavaScript forces developers to choose between: +JavaScript forces developers working with numeric data in a setting where exactness matters to choose between: - Binary floats (precision errors, bugs, non-trivial knowledge of binary float problems and some countermeasures that may not always work) - Userland libraries (bundle size, coordination, performance) From 1fe2f27025428994ef54e12ebf46c891f39a4b5e Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Thu, 23 Oct 2025 15:19:40 +0200 Subject: [PATCH 9/9] Add links --- README.md | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 716f51d2..ddb2fa84 100644 --- a/README.md +++ b/README.md @@ -209,25 +209,21 @@ Adding decimal arithmetic would not be an instance of JS breaking new ground. Ma | Language | Type Name | Location | Year Added | Notes | |----------|-----------|----------|------------|-------| -| Python | `decimal.Decimal` | Standard library | 2003 (Python 2.3) | Based on [General Decimal Arithmetic Specification](http://speleotrove.com/decimal/) | -| Java | `java.math.BigDecimal` | Standard library | 1998 (JDK 1.1) | Arbitrary precision, widely used for financial calculations | -| C# | `decimal` | Primitive type | 2000 (C# 1.0) | 128-bit decimal type, first-class language support | -| Swift | `Decimal` (formerly `NSDecimalNumber`) | Foundation framework | 2016 (Swift 3.0) | Standard library type for financial calculations | -| Ruby | `BigDecimal` | Standard library | 1999 (Ruby 1.6) | Arbitrary precision decimal arithmetic | +| Python | [`decimal.Decimal`](https://docs.python.org/3/library/decimal.html) | Standard library | 2003 (Python 2.3) | Based on [General Decimal Arithmetic Specification](http://speleotrove.com/decimal/) | +| Java | [`java.math.BigDecimal`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html) | Standard library | 1998 (JDK 1.1) | Arbitrary precision, widely used for financial calculations | +| C# | [`decimal`](https://learn.microsoft.com/en-us/dotnet/api/system.decimal?view=net-9.0) | Primitive structure type | 2000 (C# 1.0) | 128-bit decimal type, first-class language support | +| Swift | [`Decimal`](https://developer.apple.com/documentation/foundation/decimal) (formerly [`NSDecimalNumber`](https://developer.apple.com/documentation/foundation/nsdecimalnumber)) | Foundation framework | 2016 (Swift 3.0) | Standard library type for financial calculations | +| Ruby | [`BigDecimal`](https://ruby-doc.org/stdlib-3.1.0/libdoc/bigdecimal/rdoc/BigDecimal.html) | Standard library | 1999 (Ruby 1.6) | Arbitrary precision decimal arithmetic | SQL also has `NUMERIC`/`DECIMAL` as a built-in type, predating many programming languages. Many languages added decimal support 10+ years ago. The IEEE 754-2008 standard for Decimal128 is now 17 years old. (The 2019 edition of IEEE 754 also has decimal arithmetic.) - Indeed, multiple numeric types are not unusual. Languages ship with integers, floats, *and* decimals. Python even includes both decimals and rationals. The existence of JS's `Number` and `BigInt` doesn't preclude `Decimal`. Moreover, the need for decimal types across many languages reflects a genuine, universal need rather than a niche requirement. -### Why JavaScript Lacks Decimal Support - -JavaScript's origins as a lightweight browser scripting language meant it initially shipped with minimal numeric support (just IEEE 754 binary floats). However, JavaScript's role has fundamentally changed: +Moreover, having multiple numeric types in a language is not unusual. Languages ship with integers, floats, *and* decimals. (Some even have more numbers than that, such as built-in support for rationals.) Python even includes both decimals and rationals. The existence of JS's `Number` and `BigInt` doesn't preclude `Decimal`. Moreover, the need for decimal types across many languages reflects a genuine, universal need rather than a niche requirement. -- **Then (1995)**: Simple form validation and DOM manipulation -- **Now (2025)**: Full-stack applications, financial systems, e-commerce platforms, serverless backends, data processing pipelines +### Why JavaScript Lacks Decimal Support -The language has evolved to meet modern needs (adding `BigInt`, `async`/`await`, modules, etc.), but decimal arithmetic remains a critical gap. +JavaScript's origins as a lightweight browser scripting language meant it initially shipped with minimal numeric support (just IEEE 754 binary floats). However, JavaScript's role has fundamentally changed. Back in the 1990s, JS was focused on form validation and DOM manipulation. Now we have full-stack applications, including financial systems, e-commerce platforms, serverless backends, and data processing pipelines. The language has evolved to meet modern needs but decimal arithmetic remains an important gap. ### The Cost of Not Having Decimal @@ -237,13 +233,7 @@ Because JavaScript lacks native decimal support, developers have created numerou - [**bignumber.js**](https://www.npmjs.com/package/bignumber.js): ~800K weekly npm downloads - [**big.js**](https://www.npmjs.com/package/big.js): ~500K weekly npm downloads -There are also money-specific libraries such as [dinero.js](https://www.npmjs.com/package/dinero.js) with about 180K weekly NPM downloads. - -Each implementation has slightly different semantics, requires coordination across libraries, adds to total bundle size, presumably performs worse than native implementations could, and creates interoperability challenges. - -Compare this to BigInt: before native support, polyfills and userland big integer libraries had minimal adoption because the *need* was niche. Decimal libraries show massive adoption because the need is universal. - -Thus, Decimal standardizes existing practice. Developers are already using decimal libraries (millions of downloads/week), converting numbers to "cents" (error-prone, confusing), using `Number` incorrectly (causing bugs). Decimal doesn't ask developers to adopt something new; it offers a better way to do what they're already struggling to do. +There are also money-specific libraries such as [dinero.js](https://www.npmjs.com/package/dinero.js) with about 180K weekly NPM downloads. Each implementation has slightly different semantics, requires coordination across libraries, adds to total bundle size, presumably performs worse than native implementations could, and creates interoperability challenges. We believe Decimal will standardize existing practice. Developers are already using decimal libraries, converting numbers to "cents" (error-prone, confusing), using `Number` incorrectly/unsoundly (causing bugs). Decimal doesn't ask developers to adopt something new; it offers a better way to do what they're already struggling to do. ## Specification and standards