<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Naoyuki Umetani</title>
    <description>The latest articles on DEV Community by Naoyuki Umetani (@naoyuki_umetani).</description>
    <link>https://dev.to/naoyuki_umetani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4234427%2Fdeb4280e-490e-4979-996a-e672298bd230.jpg</url>
      <title>DEV Community: Naoyuki Umetani</title>
      <link>https://dev.to/naoyuki_umetani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naoyuki_umetani"/>
    <language>en</language>
    <item>
      <title>How Accurate Is Statistical Computation in JavaScript? — A Comparative Analysis: rStats.js vs. R</title>
      <dc:creator>Naoyuki Umetani</dc:creator>
      <pubDate>Mon, 09 Nov 2026 06:35:25 +0000</pubDate>
      <link>https://dev.to/naoyuki_umetani/how-accurate-is-statistical-computation-in-javascript-a-comparative-analysis-rstatsjs-vs-r-37gf</link>
      <guid>https://dev.to/naoyuki_umetani/how-accurate-is-statistical-computation-in-javascript-a-comparative-analysis-rstatsjs-vs-r-37gf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When performing statistical analysis in JavaScript, one of the primary concerns is: "Can the calculation results truly be trusted?" In scenarios such as the tails of probability distributions, cases involving extremely large parameters, or instances with infinitesimal probabilities, the choice of numerical computation method can lead to significant discrepancies in the results.&lt;/p&gt;

&lt;p&gt;To address this challenge directly, I am developing &lt;strong&gt;rStats.js&lt;/strong&gt;, a statistical computation library for JavaScript. In this project, I use R as a key benchmark to continuously verify the accuracy of these statistical calculations.&lt;/p&gt;

&lt;p&gt;In this article, I will explain the methodology used to compare rStats.js against R, as well as the level of precision I aim to achieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Automated Verification Against R 4.6.2
&lt;/h2&gt;

&lt;p&gt;In rStats.js, the general policy is to compare every public-facing function against R version 4.6.2. This goes beyond simply testing a few representative input values; the test suite incorporates a diverse range of input conditions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typical input values&lt;/li&gt;
&lt;li&gt;Extremely small probabilities (e.g., 1e-300)&lt;/li&gt;
&lt;li&gt;The extreme tails of distributions&lt;/li&gt;
&lt;li&gt;Boundary values ​​(e.g., 0, 1, or cases where degrees of freedom approach infinity)&lt;/li&gt;
&lt;li&gt;NaN and Infinity&lt;/li&gt;
&lt;li&gt;Negative values&lt;/li&gt;
&lt;li&gt;Extremely large parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The expected values—calculated using R—are generated via dedicated R scripts and stored as JSON fixtures. Consequently, there is no need to have R installed when running the tests; the results from R are pre-saved, allowing the JavaScript test suite to verify against them.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Precision Standards Vary by Function Type
&lt;/h2&gt;

&lt;p&gt;When evaluating the accuracy of numerical computations, applying a uniform margin of error across all functions is not appropriate. In rStats.js, the allowable margin of error is tailored to the specific nature of each calculation. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Calculation Type&lt;/th&gt;
&lt;th&gt;Relative Tolerance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Special functions, probability densities, and distribution functions&lt;/td&gt;
&lt;td&gt;1e-14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descriptive statistics&lt;/td&gt;
&lt;td&gt;1e-14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hypothesis testing&lt;/td&gt;
&lt;td&gt;1e-12  ​​&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linear models&lt;/td&gt;
&lt;td&gt;1e-12  ​​&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linear algebra&lt;/td&gt;
&lt;td&gt;1e-14 – 1e-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Numerical methods&lt;/td&gt;
&lt;td&gt;1e-14 – 1e-15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed-effects models&lt;/td&gt;
&lt;td&gt;Primarily 1e-12 – 1e-9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These values ​​are not merely design targets; they are tolerances established based on the results of running a large number of test cases.&lt;/p&gt;

&lt;p&gt;Note that not all results can be evaluated solely by simple relative error. In cases where values ​​are close to zero or where numerical cancellation occurs, evaluation is performed using absolute error or an appropriate scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. R’s Results Are Not Always Correct
&lt;/h2&gt;

&lt;p&gt;This is an aspect to which we pay particular attention during the development of rStats.js.&lt;/p&gt;

&lt;p&gt;While R is an excellent environment for statistical computing, its numerical calculations are not necessarily accurate under all conditions. In scenarios involving extremely small probabilities or exceptionally large parameter values, even R can be subject to rounding errors or loss of precision.&lt;/p&gt;

&lt;p&gt;Therefore, if our results do not match those of R, we do not automatically assume that the error lies with rStats.js. Instead, we calculate high-precision reference values ​​when necessary to verify which result is mathematically correct.&lt;/p&gt;

&lt;p&gt;To calculate these reference values, we primarily use the following methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arbitrary-precision arithmetic (256–1300 bits) via Rmpfr&lt;/li&gt;
&lt;li&gt;Exact rational number arithmetic via gmp&lt;/li&gt;
&lt;li&gt;High-precision computation via mpmath&lt;/li&gt;
&lt;li&gt;Independent verification using numerical integration or mathematically derived series&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, if the value of a probability distribution function does not match R’s output, but it does align with our high-precision reference value, we consider that result to be "more accurate than R." ## 4. Cases where more accurate results are returned than in R&lt;/p&gt;

&lt;p&gt;Here are a few specific examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  4-1. Extremely small probabilities
&lt;/h3&gt;

&lt;p&gt;Consider the following calculation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight r"&gt;&lt;code&gt;&lt;span class="n"&gt;pgamma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1e-100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower.tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log.p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;R result: &lt;code&gt;-1.6666666666665175e-301&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;High-precision reference value: &lt;code&gt;-1.6666666666666667e-301&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;rStats.js: &lt;code&gt;-1.666666666666667e-301&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case, the result from rStats.js is much closer to the high-precision reference value.&lt;/p&gt;

&lt;h3&gt;
  
  
  4-2. Non-central t-distribution calculations
&lt;/h3&gt;

&lt;p&gt;For the non-central t-distribution, there are cases where R fails to correctly calculate extremely small probabilities. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight r"&gt;&lt;code&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ncp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;R: &lt;code&gt;1.009e-303&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;High-precision reference value: &lt;code&gt;1.0887e-287&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;rStats.js: Matches the high-precision reference value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As shown here, there are instances where simply replicating R's output is insufficient.&lt;/p&gt;

&lt;p&gt;Note that the values ​​presented here are taken from the verification examples provided in &lt;code&gt;docs/differences-from-r.md&lt;/code&gt; on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Evaluating quantile functions using a different approach
&lt;/h2&gt;

&lt;p&gt;Quantile functions for probability distributions require a different evaluation method than standard functions. In regions where the cumulative distribution function is very flat, even a tiny difference in probability can lead to a significant discrepancy in the quantile value. Therefore, simply comparing the output against R's results using relative error is not an appropriate approach. rStats.js employs a "backward error" approach, where the result of a quantization function is fed back into the corresponding distribution function for evaluation. In other words, it assesses how closely the value returned by rStats.js—when substituted back into the distribution function—approximates the target probability.&lt;/p&gt;

&lt;p&gt;Even if a result differs from that of R, it is considered acceptable provided the backward error is equal to or less than that of R. For discrete distributions, it is also verified that the function returns the smallest integer that satisfies the definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Verification of Random Number Generation
&lt;/h2&gt;

&lt;p&gt;In addition to statistical functions, rStats.js implements random number generation. It includes a &lt;code&gt;setSeed()&lt;/code&gt; function (analogous to R’s &lt;code&gt;set.seed()&lt;/code&gt;) and supports various random number generation algorithms, such as Mersenne-Twister and L'Ecuyer-CMRG.&lt;/p&gt;

&lt;p&gt;For random number generation, the following aspects are verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discrete random numbers and sampling results match those of R.&lt;/li&gt;
&lt;li&gt;The state of the L'Ecuyer-CMRG generator matches that of R.&lt;/li&gt;
&lt;li&gt;Continuous random number values ​​fall within an acceptable margin of error.&lt;/li&gt;
&lt;li&gt;The total count of random numbers consumed matches that of R.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For discrete results, an exact match is required, while for continuous results, the standard is generally a deviation of no more than 1e-12.&lt;/p&gt;

&lt;p&gt;Note that for continuous random numbers, results may not match down to the least significant bit; this is due to differences between JavaScript’s &lt;code&gt;Math&lt;/code&gt; functions and the mathematical libraries used by the R runtime environment. Such discrepancies are intentional and are considered to be within an acceptable range.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Documenting Cases Where Results Differ from R
&lt;/h2&gt;

&lt;p&gt;The goal of rStats.js is not necessarily to achieve an exact match with R in every instance. Because the priority is to ensure mathematically and statistically correct results, the library may occasionally produce outputs that differ from those of R.&lt;/p&gt;

&lt;p&gt;Rather than silently excluding such cases from the test suite, these instances are documented as &lt;code&gt;knownDifferences&lt;/code&gt;—with the underlying reasons explained—and are made publicly available in the &lt;code&gt;docs/differences-from-r.md&lt;/code&gt; file on GitHub. In some cases—such as when R returns &lt;code&gt;NaN&lt;/code&gt; or &lt;code&gt;Infinity&lt;/code&gt; for undefined results—rStats.js is designed to throw an error instead. By explicitly documenting these discrepancies, we ensure that users are aware of how the library’s behavior differs from that of R.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Implementation Supporting Numerical Precision
&lt;/h2&gt;

&lt;p&gt;To achieve this level of precision, rStats.js employs not only standard double-precision floating-point arithmetic but also "Double-Double" arithmetic where necessary. Double-Double arithmetic combines two double-precision floating-point numbers to provide a greater number of significant digits than is otherwise possible.&lt;/p&gt;

&lt;p&gt;For instance, this technique is used in calculations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computing the exponent part of exponential functions.&lt;/li&gt;
&lt;li&gt;Evaluating continued fractions for incomplete beta functions.&lt;/li&gt;
&lt;li&gt;Evaluating continued fractions for incomplete gamma functions.&lt;/li&gt;
&lt;li&gt;Asymptotic expansions for large parameters.&lt;/li&gt;
&lt;li&gt;Operations prone to catastrophic cancellation (loss of precision).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than applying high-precision arithmetic to every calculation, we limit its use to areas where precision is a critical concern, thereby minimizing the impact on the performance of standard calculations.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Transparency Regarding Validation Results and Limitations
&lt;/h2&gt;

&lt;p&gt;rStats.js provides full transparency regarding its validation methods and its differences from R via GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/numero-uno-jpn/rStats.js/blob/main/docs/accuracy.md" rel="noopener noreferrer"&gt;Accuracy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/numero-uno-jpn/rStats.js/blob/main/docs/differences-from-r.md" rel="noopener noreferrer"&gt;Differences from R&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/numero-uno-jpn/rStats.js" rel="noopener noreferrer"&gt;Source code and tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within the scope of our validation, no known precision issues have been identified. However, this does not constitute a guarantee of mathematical perfection for all possible input values. Certain precision limitations remain outside the validated range—for example, when dealing with Bessel functions of extremely high order.&lt;/p&gt;

&lt;p&gt;We believe that what matters most is not merely claiming high precision, but being transparent about the validation methods used and the inherent limitations of the implementation. ## Conclusion&lt;/p&gt;

&lt;p&gt;I believe that JavaScript is not merely a language for developing web applications; it can also serve as a platform for performing reliable statistical computations.&lt;/p&gt;

&lt;p&gt;In rStats.js, we verify the accuracy of numerical calculations by using R as a key benchmark, alongside high-precision reference values. Moving forward, I intend to not only expand the range of available statistical methods but also further enhance the reliability and verifiability of these numerical computations.&lt;/p&gt;

&lt;p&gt;rStats.js is an open-source project licensed under the MIT License.&lt;br&gt;
GitHub: &lt;a href="https://github.com/numero-uno-jpn/rStats.js/" rel="noopener noreferrer"&gt;https://github.com/numero-uno-jpn/rStats.js/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I welcome feedback from developers interested in statistical computing, numerical analysis, and data analysis using JavaScript.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>statistics</category>
      <category>r</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Achieving Precision Equal to or Greater Than R in JavaScript: Fundamentals of Double-Double Arithmetic and Its Usage in rStats.js</title>
      <dc:creator>Naoyuki Umetani</dc:creator>
      <pubDate>Mon, 09 Nov 2026 06:35:05 +0000</pubDate>
      <link>https://dev.to/naoyuki_umetani/achieving-precision-equal-to-or-greater-than-r-in-javascript-fundamentals-of-double-double-3gh2</link>
      <guid>https://dev.to/naoyuki_umetani/achieving-precision-equal-to-or-greater-than-r-in-javascript-fundamentals-of-double-double-3gh2</guid>
      <description>&lt;p&gt;rStats.js is a statistics library for JavaScript and the browser. Its function names, arguments, default values, and return values ​​are modeled after R, and every function has been tested by comparing its output against that of R. It covers areas such as probability distributions, random number generation, various statistical tests, analysis of variance (ANOVA), linear models, mixed-effects models, and survival analysis. It has no external dependencies and is released under the MIT License.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/numero-uno-jpn/rStats.js" rel="noopener noreferrer"&gt;https://github.com/numero-uno-jpn/rStats.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo: &lt;a href="https://numero-uno-jpn.github.io/rStats.js/demo/" rel="noopener noreferrer"&gt;https://numero-uno-jpn.github.io/rStats.js/demo/&lt;/a&gt; (You can compare the results of all functions against the values ​​produced by R.)&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;npm install @numero-uno-jpn/rstats&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The background behind its creation and an overall introduction can be found in my &lt;a href="https://qiita.com/nume/items/2e2fc73e457eccb44260" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use "double-double" arithmetic?
&lt;/h2&gt;

&lt;p&gt;In JavaScript, numbers are represented as 64-bit doubles, which provide a precision of approximately 16 decimal digits. When performing calculations using standard doubles, the discrepancy from the true value can sometimes exceed 1e-14—for instance, in the tails of probability distributions. rStats.js addresses this by employing "double-double" arithmetic (combining two doubles to achieve approximately 32 digits of precision) for these specific cases, ensuring accuracy equal to or better than that of R.&lt;/p&gt;

&lt;p&gt;In this article, I will explain the fundamentals of double-double arithmetic using JavaScript code, describe where and how it is applied within rStats.js, and discuss how I optimized the performance of these otherwise computationally intensive operations.&lt;/p&gt;

&lt;p&gt;When I asked an AI how to achieve precision equal to or better than that of R, it suggested "double-double" arithmetic. While I was aware of the technique, I had never used it before. Although the concept made sense immediately, I was concerned that it might be too computationally expensive. In fact, this became an issue (discussed later).&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculations with &lt;code&gt;double&lt;/code&gt; (double-precision floats) result in more errors than one might expect
&lt;/h2&gt;

&lt;p&gt;Let’s start with a well-known example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0.30000000000000004&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When adding values ​​that differ significantly in magnitude, the smaller value is lost due to rounding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 0 (the correct result is 1)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as 1 is added to 1e16, the 1 is lost due to rounding.&lt;/p&gt;

&lt;p&gt;The same thing happens in R. While R’s &lt;code&gt;sum&lt;/code&gt; function may utilize &lt;code&gt;long double&lt;/code&gt;—which offers higher precision than &lt;code&gt;double&lt;/code&gt;—if the environment supports it, the actual level of precision varies by environment. In the version of R (4.5.2) I used on Windows to write this article, &lt;code&gt;.Machine$longdouble.digits&lt;/code&gt; was 53, meaning it had the same precision as a standard &lt;code&gt;double&lt;/code&gt;. Consequently, the following sum is incorrect in this R environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight r"&gt;&lt;code&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1e20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1e3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1e-10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;-1e20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;-1e3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# -1000 (the correct result is 1e-10)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In contrast, the &lt;code&gt;sum&lt;/code&gt; function in rStats.js returns &lt;code&gt;1e-10&lt;/code&gt; for this calculation.&lt;/p&gt;

&lt;p&gt;While developing rStats.js, I was surprised to discover quite a few instances where R’s own results were imprecise. I have compiled these findings in a document titled &lt;a href="https://github.com/numero-uno-jpn/rStats.js/blob/main/docs/ja/differences-from-r.md" rel="noopener noreferrer"&gt;Differences from R&lt;/a&gt;. Furthermore, the fact that the precision of intermediate calculations in R can vary depending on the environment was, in itself, a surprise. &lt;a href="https://github.com/r-wasm/webr" rel="noopener noreferrer"&gt;WebR&lt;/a&gt; is an implementation of R compiled into WebAssembly, allowing it to run in browsers or within Node.js. Since WebAssembly instructions only support 32-bit and 64-bit floating-point numbers, WebR performs all calculations using 64-bit doubles. In the version of R for Windows that I examined, the precision of &lt;code&gt;long double&lt;/code&gt; was also identical to that of &lt;code&gt;double&lt;/code&gt;. Learning about these types of numerical errors convinced me of the utility of &lt;code&gt;rStats.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fundamentals of Double-Double Arithmetic
&lt;/h2&gt;

&lt;p&gt;Double-double arithmetic represents a single number as the sum of two &lt;code&gt;double&lt;/code&gt; values: &lt;code&gt;hi + lo&lt;/code&gt;. The &lt;code&gt;hi&lt;/code&gt; component holds the bulk of the value, while the &lt;code&gt;lo&lt;/code&gt; component captures the remainder that cannot be represented by &lt;code&gt;hi&lt;/code&gt; alone. This approach yields a precision of approximately 106 bits (roughly 32 decimal digits).&lt;/p&gt;

&lt;p&gt;The foundation for this lies in the "error-free transformations" described by Dekker (1971) and Knuth (in &lt;em&gt;The Art of Computer Programming&lt;/em&gt;, section 4.2.2). These methods allow for the precise decomposition of an arithmetic operation (such as addition or multiplication) into two parts: the rounded result and the error term discarded during the rounding process. ### Addition: twoSum&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Returns [s, e] such that a + b = s + e holds exactly&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;twoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;twoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// [0.30000000000000004, -2.7755575615628914e-17]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;s&lt;/code&gt; is the result of a standard addition, and &lt;code&gt;e&lt;/code&gt; represents the portion lost due to rounding. In the first example, the sum can be corrected by adding back the lost portion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiplication: twoProd
&lt;/h3&gt;

&lt;p&gt;To extract the error from a multiplication, the simplest approach in many other languages ​​is to use FMA (an instruction that performs a fused multiply-add, with only a single rounding step). However, JavaScript lacks an FMA operation. Instead, we use Veltkamp’s method to split each number into two parts—a high part and a low part—and then calculate their products without rounding. Since the product of two 26-bit values ​​fits within 52 bits, it can be represented exactly using a double-precision float (which has a 53-bit significand).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SPLITTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;134217729&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 2^27 + 1&lt;/span&gt;

&lt;span class="c1"&gt;// Splits 'a' into 'hi' and 'lo' components, each with a 26-bit significand (a = hi + lo)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SPLITTER&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Returns [p, e] such that a * b = p + e holds exactly&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;twoProd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ah&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;al&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ah&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;bh&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ah&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;bl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;al&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;al&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;twoProd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [0.010000000000000002, -8.326672684688674e-19]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
javascript&lt;/p&gt;

&lt;p&gt;Addition, multiplication, and division for "double-double" numbers are constructed by combining these two operations. As an example, addition is implemented as follows—this follows the same procedure as &lt;code&gt;ddAdd&lt;/code&gt; in rStats.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A version of twoSum with fewer steps; valid when |a| &amp;gt;= |b|&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;quickTwoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Addition of two double-double numbers: (ah + al) + (bh + bl)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ddAdd&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;ah&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;al&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ah&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;quickTwoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;quickTwoSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Sum of 1e16 + 1 and -1e16 + 0.5&lt;/span&gt;
&lt;span class="nf"&gt;ddAdd&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;  &lt;span class="c1"&gt;// [1.5, 0]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach uses &lt;code&gt;twoSum&lt;/code&gt; to add the high-order components (&lt;code&gt;ah&lt;/code&gt; and &lt;code&gt;bh&lt;/code&gt;) and the low-order components (&lt;code&gt;al&lt;/code&gt; and &lt;code&gt;bl&lt;/code&gt;) separately. It then incorporates the resulting error terms step-by-step, finally formatting the result back into a high-order (&lt;code&gt;hi&lt;/code&gt;) and low-order (&lt;code&gt;lo&lt;/code&gt;) pair.&lt;/p&gt;

&lt;p&gt;The code provided here is a minimal implementation intended to illustrate the underlying mechanism. The implementation in rStats.js (&lt;a href="https://github.com/numero-uno-jpn/rStats.js/blob/main/src/special/dd.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/special/dd.ts&lt;/code&gt;&lt;/a&gt;) includes additional handling for edge cases: if an overflow occurs, the error term is set to 0; if a value is so large that &lt;code&gt;SPLITTER * a&lt;/code&gt; would overflow, the value is scaled down (by a power of 2) to bring it closer to 1 before the split is performed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage in rStats.js
&lt;/h2&gt;

&lt;p&gt;rStats.js does not use double-double arithmetic for every calculation. It is employed only in scenarios where standard double-precision floating-point numbers are insufficient to achieve the required precision of 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;14&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. For example, the following cases are relevant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exponentials of small probabilities:&lt;/strong&gt; When calculating probabilities in the form 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop"&gt;exp&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;−&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, the absolute error in 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 translates directly into a relative error in the probability. As the probability approaches 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;300&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 reaches several hundred, and the error using &lt;code&gt;double&lt;/code&gt; precision reaches 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. At 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;700&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, the mere act of rounding 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 introduces an error of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;14&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continued fractions for the incomplete beta function:&lt;/strong&gt; When parameters are large and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is near the mean, the result becomes highly sensitive to the rounding of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. For 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;a&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.999&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, using only &lt;code&gt;double&lt;/code&gt; precision results in a deviation of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Summations for statistical tests:&lt;/strong&gt; Calculations for means, variances, and correlations are performed using &lt;code&gt;double-double&lt;/code&gt; precision.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conversely, in cases where the upper bound of the error indicates that standard &lt;code&gt;double&lt;/code&gt; precision is sufficient, the &lt;code&gt;double-double&lt;/code&gt; operations are omitted.&lt;/p&gt;

&lt;p&gt;The impact of this can be verified by disabling &lt;code&gt;double-double&lt;/code&gt; arithmetic internally. When disabled, only about half of the test results maintain a precision of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;14&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. In some cases, the degradation is significant: the beta function (&lt;code&gt;beta&lt;/code&gt;) shows a deviation of up to 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, and the &lt;code&gt;pt&lt;/code&gt; function (Student's t-distribution) deviates by up to 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;

&lt;p&gt;The absence of FMA (Fused Multiply-Add) in JavaScript also influences the choice of R—the reference implementation used for comparison. The &lt;code&gt;arm64&lt;/code&gt; version of R utilizes FMA, which causes the final digit of the second value generated by &lt;code&gt;set.seed(42); rnorm(3)&lt;/code&gt; to differ from that of the &lt;code&gt;x86_64&lt;/code&gt; version. rStats.js...&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>algorithms</category>
      <category>r</category>
    </item>
    <item>
      <title>Why I Built rStats.js: Bringing Reliable Statistical Computing to JavaScript</title>
      <dc:creator>Naoyuki Umetani</dc:creator>
      <pubDate>Mon, 09 Nov 2026 06:13:46 +0000</pubDate>
      <link>https://dev.to/naoyuki_umetani/why-i-built-rstatsjs-bringing-reliable-statistical-computing-to-javascript-4de6</link>
      <guid>https://dev.to/naoyuki_umetani/why-i-built-rstatsjs-bringing-reliable-statistical-computing-to-javascript-4de6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I am developing &lt;strong&gt;rStats.js&lt;/strong&gt;, an open-source library for performing statistical analysis in JavaScript.&lt;/p&gt;

&lt;p&gt;My goal is to make it easy to access numerically accurate and statistically sound statistical computations from within JavaScript applications.&lt;/p&gt;

&lt;p&gt;JavaScript is used not only in web browsers but also in a wide range of other environments, such as Node.js. Meanwhile, R and Python are the dominant languages ​​in the field of statistical analysis; I felt that the options for performing robust statistical computing in JavaScript still had significant room for improvement.&lt;/p&gt;

&lt;p&gt;Consequently, I decided to develop a JavaScript library that focuses not just on implementing statistical methods, but also on the precision of numerical calculations and the validity of the underlying algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Develop My Own Instead of Using Existing Statistical Libraries?
&lt;/h2&gt;

&lt;p&gt;There are already several statistical libraries available for JavaScript. I have examined many of these existing libraries while working on my own project.&lt;/p&gt;

&lt;p&gt;In doing so, I placed a high priority on the accuracy and verifiability of statistical computations.&lt;/p&gt;

&lt;p&gt;Simply having a statistical function implemented is not the same as ensuring that its output is correct under various conditions. In fields like medical research or the analysis of clinical data, even a minor discrepancy in a calculation result could affect how research findings are interpreted.&lt;/p&gt;

&lt;p&gt;With rStats.js, I emphasize the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adopting statistically sound algorithms.&lt;/li&gt;
&lt;li&gt;Verifying the precision and stability of numerical computations.&lt;/li&gt;
&lt;li&gt;Using R’s statistical functions as a key benchmark.&lt;/li&gt;
&lt;li&gt;Ensuring correct behavior even with boundary values ​​or extreme inputs.&lt;/li&gt;
&lt;li&gt;Providing an API that is easy to integrate into real-world applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using R as a Key Reference
&lt;/h2&gt;

&lt;p&gt;In the world of statistical analysis, R is a cornerstone.&lt;/p&gt;

&lt;p&gt;With rStats.js, I aim to enable similar types of analysis in JavaScript by using R’s functions and statistical methods as a reference.&lt;/p&gt;

&lt;p&gt;However, the goal is not to blindly replicate R’s behavior in every instance. We conduct comparative verification against R, while also utilizing high-precision reference values ​​whenever necessary. In cases where our results differ from those produced by R, we prioritize the outcome that is mathematically and statistically correct.&lt;/p&gt;

&lt;p&gt;Our focus is not merely on replicating the results of existing software, but on ensuring that the results themselves are accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing Statistical Analysis to JavaScript Applications
&lt;/h2&gt;

&lt;p&gt;A primary goal of rStats.js is to enable the integration of statistical analysis capabilities not only into dedicated statistical software but also into general-purpose web applications and business systems.&lt;/p&gt;

&lt;p&gt;Potential use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BI and data analysis tools&lt;/li&gt;
&lt;li&gt;Survey and research platforms&lt;/li&gt;
&lt;li&gt;Medical and clinical research systems&lt;/li&gt;
&lt;li&gt;Educational and learning applications&lt;/li&gt;
&lt;li&gt;Browser-based statistical analysis tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performing statistical calculations directly within a browser offers significant advantages regarding privacy and data management, as it eliminates the need to transmit data to an external server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Outlook
&lt;/h2&gt;

&lt;p&gt;We are currently continuing to expand our range of statistical methods and verify our numerical computations.&lt;/p&gt;

&lt;p&gt;Moving forward, we aim to cover a broader spectrum of statistical techniques. We are also committed to enhancing API stability, improving documentation, expanding our test suite, and providing practical code samples—all with the goal of creating a library that developers can rely on with confidence.&lt;/p&gt;

&lt;p&gt;Ultimately, we aspire to see this library adopted not only for individual research and study but also integrated into other open-source projects and commercial applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;rStats.js&lt;/strong&gt; is an open-source project released under the MIT License.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/numero-uno-jpn/rStats.js/" rel="noopener noreferrer"&gt;https://github.com/numero-uno-jpn/rStats.js/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We welcome feedback and contributions from developers looking to perform statistical calculations in JavaScript, as well as from anyone with an interest in statistical algorithms and numerical computation.&lt;/p&gt;

&lt;p&gt;Please take a look at the project!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>statistics</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
