<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.thewritepath.net/blog</id>
    <title>writePath Blog</title>
    <updated>2026-10-27T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://www.thewritepath.net/blog"/>
    <subtitle>writePath Blog</subtitle>
    <icon>https://www.thewritepath.net/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Bugs we found in DuckDB, ClickHouse, and NumPy aggregate functions]]></title>
        <id>https://www.thewritepath.net/blog/aggregate-function-vendor-bugs</id>
        <link href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs"/>
        <updated>2026-10-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[While building writePath we found rounding, range, and logic bugs in vendor aggregate functions. Here is how we handle each type and where we track every report.]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-were-building">What we're building<a href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs#what-were-building" class="hash-link" aria-label="Direct link to What we're building" title="Direct link to What we're building" translate="no">​</a></h2>
<p>We have been developing writePath over the past year focused on providing customers with:</p>
<ol>
<li class="">Sub-millisecond aggregations over the first or last N rows of their data with no pre-computation</li>
<li class="">Precise aggregation functions validated against reference implementations and exact math</li>
<li class="">A simple AWS Marketplace experience allowing control through APIs</li>
</ol>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-we-found">What we found<a href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs#what-we-found" class="hash-link" aria-label="Direct link to What we found" title="Direct link to What we found" translate="no">​</a></h2>
<p>During development we used reference implementations from vendors and packages such as DuckDB, ClickHouse, and NumPy to validate aggregate correctness.</p>
<p>We found bugs within the reference implementations falling into one of 3 main categories: Rounding, Range, and Logic.</p>
<table><thead><tr><th>Category</th><th>Reference implementation bug</th><th>How writePath addresses it</th></tr></thead><tbody><tr><td>Rounding</td><td>Digits rounded during accumulation are lost</td><td>Values are accumulated exactly and rounded once at the end</td></tr><tr><td>Range</td><td>Numbers exceed type constraints resulting in overflow or wraparound</td><td>Integers stay integers and intermediate results never overflow when the true answer fits</td></tr><tr><td>Logic</td><td>The wrong rule is applied for constant data, infinities, zero or negative weights, or empty input</td><td>Every edge case has a defined rule that is documented per function and validated against reference implementations and exact math</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-we-report-bugs">How we report bugs<a href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs#how-we-report-bugs" class="hash-link" aria-label="Direct link to How we report bugs" title="Direct link to How we report bugs" translate="no">​</a></h2>
<p>Since these are not security bugs, our approach is to submit each bug to the vendor through proper channels and then post our findings here.</p>
<p>After each submission and post, we will add another entry to the table at the bottom of this post.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-us">About Us<a href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs#about-us" class="hash-link" aria-label="Direct link to About Us" title="Direct link to About Us" translate="no">​</a></h2>
<p>writePath is an AWS Marketplace SaaS for sub-millisecond aggregations over the first or last N rows of your data with no pre-computation. It offers 65 high-precision aggregation functions tested against reference implementations.</p>
<p><a href="https://www.thewritepath.net/" target="_blank" rel="noopener noreferrer" class="">www.thewritepath.net</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="all-vendor-bugs">All Vendor Bugs<a href="https://www.thewritepath.net/blog/aggregate-function-vendor-bugs#all-vendor-bugs" class="hash-link" aria-label="Direct link to All Vendor Bugs" title="Direct link to All Vendor Bugs" translate="no">​</a></h2>
<table><thead><tr><th>Vendor</th><th>Function</th><th>Bug Submission Link</th><th>Blog Post</th><th>Date</th></tr></thead><tbody><tr><td>DuckDB</td><td>favg</td><td><a href="https://github.com/duckdb/duckdb/issues/27961" target="_blank" rel="noopener noreferrer" class="">duckdb/duckdb#27961</a></td><td><a class="" href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude">DuckDB favg returns the wrong average when values differ greatly in magnitude</a></td><td>October 27, 2026</td></tr></tbody></table>]]></content>
        <category label="Vendor bugs" term="Vendor bugs"/>
        <category label="Aggregate functions" term="Aggregate functions"/>
        <category label="DuckDB" term="DuckDB"/>
        <category label="ClickHouse" term="ClickHouse"/>
        <category label="NumPy" term="NumPy"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[DuckDB favg returns the wrong average when values differ greatly in magnitude]]></title>
        <id>https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude</id>
        <link href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude"/>
        <updated>2026-10-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[DuckDB 2.0.0 favg returns 0.0 instead of 0.5 when values differ greatly in magnitude, as in 1, 1e100, 1, −1e100.]]></summary>
        <content type="html"><![CDATA[<p>In DuckDB 1.5.5 and 2.0.0, <code>favg</code> returns the wrong average when values differ greatly in magnitude. Over the four doubles 1, 1e100, 1, and −1e100, it returns 0.0. It should return 0.5.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reproduce-it">Reproduce it<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#reproduce-it" class="hash-link" aria-label="Direct link to Reproduce it" title="Direct link to Reproduce it" translate="no">​</a></h2>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> favg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">DOUBLE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain">e100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain">e100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>DuckDB 2.0.0 returns:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">0.0</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-correct-answer">The correct answer<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#the-correct-answer" class="hash-link" aria-label="Direct link to The correct answer" title="Direct link to The correct answer" translate="no">​</a></h2>
<p>The correct answer is 0.5. 1e100 − 1e100 + 1 + 1 = 2. The mean of the four values is 2 / 4 = 0.5. The Python code determines the exact mean with fractions and rounds it once to a double:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> fractions </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Fraction </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> F</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">favg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">values</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># the exact mean, rounded once to a double</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">F</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> v </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">values</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">favg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1e100</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1e100</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 0.5</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-duckdb-gets-it-wrong">Why DuckDB gets it wrong<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#why-duckdb-gets-it-wrong" class="hash-link" aria-label="Direct link to Why DuckDB gets it wrong" title="Direct link to Why DuckDB gets it wrong" translate="no">​</a></h2>
<p>A double can only hold the equivalent of around 16 decimal digits of precision. Adding 1 to 1e100 requires more precision.</p>
<p>DuckDB's <code>favg</code> uses Kahan summation in an attempt to prevent this precision loss. However, it only catches the loss when the running sum is the larger number in the summation step. If the running sum is the smaller number, it is unable to catch the precision loss.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="kahan-algorithm">Kahan Algorithm<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#kahan-algorithm" class="hash-link" aria-label="Direct link to Kahan Algorithm" title="Direct link to Kahan Algorithm" translate="no">​</a></h3>
<ol>
<li class="">adjusted value = value − correction</li>
<li class="">new sum = adjusted value + old sum</li>
<li class="">correction = (new sum − old sum) − adjusted value</li>
</ol>
<table><thead><tr><th style="text-align:right">Value</th><th style="text-align:right">Adj&nbsp;Val</th><th style="text-align:right">Sum</th><th style="text-align:right">Correction</th><th style="text-align:left">Explanation</th></tr></thead><tbody><tr><td style="text-align:right">—</td><td style="text-align:right">—</td><td style="text-align:right">0</td><td style="text-align:right">0</td><td style="text-align:left">init</td></tr><tr><td style="text-align:right">1</td><td style="text-align:right">1</td><td style="text-align:right">1</td><td style="text-align:right">0</td><td style="text-align:left">First number into accumulator</td></tr><tr><td style="text-align:right">1e100</td><td style="text-align:right">1e100</td><td style="text-align:right">1e100</td><td style="text-align:right">0</td><td style="text-align:left">We lost the 1 here due to precision</td></tr><tr><td style="text-align:right">1</td><td style="text-align:right">1</td><td style="text-align:right">1e100</td><td style="text-align:right">−1</td><td style="text-align:left">We caught the −1 since 1e100 cancel out</td></tr><tr><td style="text-align:right">−1e100</td><td style="text-align:right">−1e100</td><td style="text-align:right">0</td><td style="text-align:right">0</td><td style="text-align:left">The −1 correction is lost during addition due to precision</td></tr></tbody></table>
<p>Therefore the average is 0/4 = 0.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-fix-it">How to fix it<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#how-to-fix-it" class="hash-link" aria-label="Direct link to How to fix it" title="Direct link to How to fix it" translate="no">​</a></h2>
<p>Shewchuk summation keeps the sum exact by storing it as a short list of doubles called partials.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="shewchuk-algorithm">Shewchuk Algorithm<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#shewchuk-algorithm" class="hash-link" aria-label="Direct link to Shewchuk Algorithm" title="Direct link to Shewchuk Algorithm" translate="no">​</a></h3>
<ol>
<li class="">Start with an empty partial list</li>
<li class="">If a new value can't be added to an existing partial without loss of precision → then keep it as its own partial → else add it to an existing partial</li>
<li class="">If adding to an existing partial results in zero, remove it</li>
</ol>
<p>Final Step: result = sum of the partials, rounded once</p>
<table><thead><tr><th style="text-align:right">Value</th><th style="text-align:left">Partials</th><th style="text-align:left">Explanation</th></tr></thead><tbody><tr><td style="text-align:right">—</td><td style="text-align:left">[ ]</td><td style="text-align:left">init</td></tr><tr><td style="text-align:right">1</td><td style="text-align:left">[1]</td><td style="text-align:left">First number into accumulator</td></tr><tr><td style="text-align:right">1e100</td><td style="text-align:left">[1, 1e100]</td><td style="text-align:left">The 1 can't add to 1e100 partial without losing precision, so it stays a partial</td></tr><tr><td style="text-align:right">1</td><td style="text-align:left">[2, 1e100]</td><td style="text-align:left">The 1 is added to the 1 partial</td></tr><tr><td style="text-align:right">−1e100</td><td style="text-align:left">[2]</td><td style="text-align:left">1e100 cancels and its partial is removed</td></tr></tbody></table>
<p>Result: 2, and the average is 2/4 = 0.5.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vendor-issue">Vendor issue<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#vendor-issue" class="hash-link" aria-label="Direct link to Vendor issue" title="Direct link to Vendor issue" translate="no">​</a></h2>
<p>Reported to DuckDB as <a href="https://github.com/duckdb/duckdb/issues/27961" target="_blank" rel="noopener noreferrer" class="">duckdb/duckdb#27961</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="writepath">writePath<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#writepath" class="hash-link" aria-label="Direct link to writePath" title="Direct link to writePath" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="favg-function">FAVG function<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#favg-function" class="hash-link" aria-label="Direct link to FAVG function" title="Direct link to FAVG function" translate="no">​</a></h3>
<p>writePath's <a class="" href="https://www.thewritepath.net/aggregations#favg">FAVG</a> uses Shewchuk summation. No precision is lost due to the way partials are handled. The sum is rounded only once at the end, so the result is the most precise double. The function ensures results are always the same regardless of row order.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-us">About us<a href="https://www.thewritepath.net/blog/duckdb-favg-wrong-average-values-differ-in-magnitude#about-us" class="hash-link" aria-label="Direct link to About us" title="Direct link to About us" translate="no">​</a></h3>
<p>writePath is an AWS Marketplace SaaS for sub-millisecond aggregations over the first or last N rows of your data with no pre-computation. It offers 65 high-precision aggregation functions tested against reference implementations.</p>
<p><a href="https://www.thewritepath.net/" target="_blank" rel="noopener noreferrer" class="">www.thewritepath.net</a></p>]]></content>
        <category label="DuckDB" term="DuckDB"/>
        <category label="favg" term="favg"/>
    </entry>
</feed>