Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-02-02 Thread Dean Rasheed
On Tue, 31 Jan 2023 at 21:59, Joel Jacobson  wrote:
>
> Nice, you managed to simplify it even further.
> I think the comment and the code now are crystal clear together.
>
> I've tested it successfully, test report attached.
>

Cool. Thanks for testing.
Committed.

Regards,
Dean




Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-31 Thread Joel Jacobson
On Tue, Jan 31, 2023, at 20:25, Dean Rasheed wrote:
> That seems a bit wordy, given the context of this comment. I think
> it's sufficient to just give the formula, and note that it simplifies
> when DEC_DIGITS is even (not just 4):
>
> /*
>  * Assume the input was normalized, so arg.weight is accurate.  The result
>  * then has at least sweight = floor(arg.weight * DEC_DIGITS / 2 + 1)
>  * digits before the decimal point.  When DEC_DIGITS is even, we can save
>  * a few cycles, since the division is exact and there is no need to
>  *  round down.
>  */
> #if DEC_DIGITS == ((DEC_DIGITS / 2) * 2)
> sweight = arg.weight * DEC_DIGITS / 2 + 1;
> #else
> if (arg.weight >= 0)
> sweight = arg.weight * DEC_DIGITS / 2 + 1;
> else
> sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;
> #endif

Nice, you managed to simplify it even further.
I think the comment and the code now are crystal clear together.

I've tested it successfully, test report attached. In summary:
DEC_DIGITS=1 now produce 16 sig. figs. in the range sqrt(2e-31) .. sqrt(2e+32), 
which before had a mix of 17 and 18 sig. figs in the result.
DEC_DIGTIS=2 now produce 16 sig. figs. in the range sqrt(2e-31) .. sqrt(2e+31), 
which before always had 17 sig. figs in the result.
DEC_DIGITS=4 is unchanged.

Exact tested patch attached, code copy/pasted verbatim from your email.

Test 

fix-sweight-v4.patch
Description: Binary data
--
-- Comparison of sig_figs(sqrt(n)) for HEAD vs fix-sweight-v4
-- when DEC_DIGITS == 1
--
 DEC_DIGITS |  n   | HEAD | fix-sweight-v4 | diff
+--+--++--
  1 | 2e-100   |   51 | 51 |0
  1 | 2e-99|   50 | 50 |0
  1 | 2e-98|   50 | 50 |0
  1 | 2e-97|   49 | 49 |0
  1 | 2e-96|   49 | 49 |0
  1 | 2e-95|   48 | 48 |0
  1 | 2e-94|   48 | 48 |0
  1 | 2e-93|   47 | 47 |0
  1 | 2e-92|   47 | 47 |0
  1 | 2e-91|   46 | 46 |0
  1 | 2e-90|   46 | 46 |0
  1 | 2e-89|   45 | 45 |0
  1 | 2e-88|   45 | 45 |0
  1 | 2e-87|   44 | 44 |0
  1 | 2e-86|   44 | 44 |0
  1 | 2e-85|   43 | 43 |0
  1 | 2e-84|   43 | 43 |0
  1 | 2e-83|   42 | 42 |0
  1 | 2e-82|   42 | 42 |0
  1 | 2e-81|   41 | 41 |0
  1 | 2e-80|   41 | 41 |0
  1 | 2e-79|   40 | 40 |0
  1 | 2e-78|   40 | 40 |0
  1 | 2e-77|   39 | 39 |0
  1 | 2e-76|   39 | 39 |0
  1 | 2e-75|   38 | 38 |0
  1 | 2e-74|   38 | 38 |0
  1 | 2e-73|   37 | 37 |0
  1 | 2e-72|   37 | 37 |0
  1 | 2e-71|   36 | 36 |0
  1 | 2e-70|   36 | 36 |0
  1 | 2e-69|   35 | 35 |0
  1 | 2e-68|   35 | 35 |0
  1 | 2e-67|   34 | 34 |0
  1 | 2e-66|   34 | 34 |0
  1 | 2e-65|   33 | 33 |0
  1 | 2e-64|   33 | 33 |0
  1 | 2e-63|   32 | 32 |0
  1 | 2e-62|   32 | 32 |0
  1 | 2e-61|   31 | 31 |0
  1 | 2e-60|   31 | 31 |0
  1 | 2e-59|   30 | 30 |0
  1 | 2e-58|   30 | 30 |0
  1 | 2e-57|   29 | 29 |0
  1 | 2e-56|   29 | 29 |0
  1 | 2e-55|   28 | 28 |0
  1 | 2e-54|   28 | 28 |0
  1 | 2e-53|   27 | 27 |0
  1 | 2e-52|   27 | 27 |0
  1 | 2e-51|   26 | 26 |0
  1 | 2e-50|   26 | 26 |0
  1 | 2e-49|   25 | 25 |0
  1 | 2e-48|   25 | 25 |0
  1 | 2e-47|   24 | 24 |0
  1 | 2e-46|   24 | 24 |0
  1 | 2e-45|   23 | 23 |0
  1 | 2e-44|   23 | 23 |0
  1 | 2e-43|   

Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-31 Thread Dean Rasheed
On Tue, 31 Jan 2023 at 15:05, Joel Jacobson  wrote:
>
> I also think the performance impact no matter how small isn't worth it,
> but a comment based on your comments would be very valuable IMO.
>
> Below is an attempt at summarising your text, and to avoid the performance 
> impact,
> maybe an #if so we get the general correct formula for DEC_DIGITS 1 or 2,
> and the reduced hand-optimised form for DEC_DIGITS 4?
> That could also improve readabilty, since readers perhaps more easily would 
> see
> the relation between sweight and arg.weight, for the only DEC_DIGITS case we 
> care about.
>

That seems a bit wordy, given the context of this comment. I think
it's sufficient to just give the formula, and note that it simplifies
when DEC_DIGITS is even (not just 4):

/*
 * Assume the input was normalized, so arg.weight is accurate.  The result
 * then has at least sweight = floor(arg.weight * DEC_DIGITS / 2 + 1)
 * digits before the decimal point.  When DEC_DIGITS is even, we can save
 * a few cycles, since the division is exact and there is no need to
 *  round down.
 */
#if DEC_DIGITS == ((DEC_DIGITS / 2) * 2)
sweight = arg.weight * DEC_DIGITS / 2 + 1;
#else
if (arg.weight >= 0)
sweight = arg.weight * DEC_DIGITS / 2 + 1;
else
sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;
#endif

Regards,
Dean




Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-31 Thread Joel Jacobson
Hi,

On Tue, Jan 31, 2023, at 14:40, Dean Rasheed wrote:
> That's still not right. If you want a proper mathematically justified
> formula, it's fairly easy to derive.
...
> or equivalently, in code with truncated integer division:
>
> if (arg.weight >= 0)
> sweight = arg.weight * DEC_DIGITS / 2 + 1;
> else
> sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;

Beautiful! Thank you for magnificent analysis and extraordinary good 
explanation, now I finally get it.

> When DEC_DIGITS = 4, this formula also reduces to sweight = 2 *
> arg.weight + 1, but neither gcc nor clang is smart enough to spot that
> (clang doesn't simplify your formula either, BTW).

Oh, that's a shame. :-(

> So even though I
> believe that the above is mathematically correct, and won't change any
> results for DEC_DIGITS = 4, I'm still hesitant to use it, because it
> will have a (small) performance impact, and I don't believe it does
> anything to improve code readability (and certainly not without an
> explanatory comment).

I also think the performance impact no matter how small isn't worth it,
but a comment based on your comments would be very valuable IMO.

Below is an attempt at summarising your text, and to avoid the performance 
impact,
maybe an #if so we get the general correct formula for DEC_DIGITS 1 or 2,
and the reduced hand-optimised form for DEC_DIGITS 4?
That could also improve readabilty, since readers perhaps more easily would see
the relation between sweight and arg.weight, for the only DEC_DIGITS case we 
care about.

Suggestion:

/*
 * Here we approximate the decimal weight of the square root (sweight),
 * given the NBASE-weight (arg.weight) of the input argument.
 *
 * The lower bound of the decimal weight of the input argument is used 
to
 * calculate the decimal weight of the square root, with integer 
division
 * being truncated.
 * 
 * The general formula is:
 *
 * sweight = floor((n+1) / 2)
 * 
 * In our case, since the base is NBASE, not 10, and since we only
 * require an approximation, we don't take the trouble to compute n
 * exactly, we just use the fact that it lies in the range
 * 
 * arg.weight * DEC_DIGITS + 1 <= n <= (arg.weight + 1) * DEC_DIGITS
 *
 * Since we want to ensure at least a certain number of significant
 * digits in the result, we're only interested in the lower bound.
 * Plugging that into the formula above gives:
 * 
 * sweight >= floor(arg.weight * DEC_DIGITS / 2 + 1)
 *
 * Which leads us to the formula below with truncated integer division.
 */
#if DEC_DIGITS == 1 || DEC_DIGITS == 2

if (arg.weight >= 0)
sweight = arg.weight * DEC_DIGITS / 2 + 1;
else
sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;

#elif DEC_DIGITS == 4

/*
 * Neither gcc nor clang is smart enough to spot that
 * the formula above neatly reduces to the below
 * when DEC_DIGITS == 4.
 */
sweight = 2 * arg.weight + 1;

#else
#error unsupported NBASE
#endif

/Joel




Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-31 Thread Dean Rasheed
On Tue, 31 Jan 2023 at 08:00, Joel Jacobson  wrote:
>
> I think this is what we want:
>
> if (arg.weight < 0)
> sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1;
> else
> sweight = arg.weight * DEC_DIGITS / 2 + 1;
>

That's still not right. If you want a proper mathematically justified
formula, it's fairly easy to derive.

Let "n" be the decimal weight of the input, taken to be the number of
decimal digits before the decimal point (or minus the number of zeros
after the decimal point, for inputs with no digits before the decimal
point).

Similarly, let "sweight" be the decimal weight of the square root.
Then the relationship between sweight and n can be seen from a few
simple examples (to 4 significant digits):

n argsqrt(arg) sweight
-30.0001 .. 0.0000.01 .. 0.03162   -1
-20.001 .. 0.00  0.03162 .. 0.0-1
-10.01 .. 0.00.1 .. 0.3162 0
0 0.1 .. 0.  0.3162 .. 0.  0
1 1 .. 9.999 1 .. 3.1621
2 10 .. 99.993.16 .. 9.999 1
3 100 .. 999.9   10 .. 31.62   2
4 1000 ...   31.62 .. 99.992

and the general formula is:

sweight = floor((n+1) / 2)

In our case, since the base is NBASE, not 10, and since we only
require an approximation, we don't take the trouble to compute n
exactly, we just use the fact that it lies in the range

arg.weight * DEC_DIGITS + 1 <= n <= (arg.weight + 1) * DEC_DIGITS

Since we want to ensure at least a certain number of significant
digits in the result, we're only interested in the lower bound.
Plugging that into the formula above gives:

sweight >= floor(arg.weight * DEC_DIGITS / 2 + 1)

or equivalently, in code with truncated integer division:

if (arg.weight >= 0)
sweight = arg.weight * DEC_DIGITS / 2 + 1;
else
sweight = 1 - (1 - arg.weight * DEC_DIGITS) / 2;

This is not the same as your formula. For example, when DEC_DIGITS = 1
and arg.weight = -1, yours gives sweight = -1 which isn't right, it
should be 0.

When DEC_DIGITS = 4, this formula also reduces to sweight = 2 *
arg.weight + 1, but neither gcc nor clang is smart enough to spot that
(clang doesn't simplify your formula either, BTW). So even though I
believe that the above is mathematically correct, and won't change any
results for DEC_DIGITS = 4, I'm still hesitant to use it, because it
will have a (small) performance impact, and I don't believe it does
anything to improve code readability (and certainly not without an
explanatory comment).

When DEC_DIGITS = 1, it does guarantee that the result has exactly 16
significant digits (or more if the input scale is larger), but that's
only really of theoretical interest to anyone.

As I noted above, when DEC_DIGITS > 1, this formula is only an
approximation, since it's not using the exact input decimal weight. So
my inclination is to leave the code as-is. It does guarantee that the
result has at least 16 significant digits, which is the intention.

Regards,
Dean




Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-31 Thread Joel Jacobson
Hi,

On Sun, Jan 29, 2023, at 14:33, Dean Rasheed wrote:
> On Sat, 28 Jan 2023 at 22:14, Joel Jacobson  wrote:
>> HEAD, patched:
>> sweight = (arg.weight * DEC_DIGITS) / 2 + 1
>
> You haven't actually said why this formula is more correct than the
> current one. I believe that it is when arg.weight >= 0, but I'm not
> convinced it's correct for arg.weight < 0.

Ops, I totally failed to consider arg.weight < 0. Nice catch, thanks!

It seems that,
when arg.weight < 0, the current sweight formula in HEAD is correct, and,
when arg.weight >= 0, the formula I suggested seems to be an improvement.

I think this is what we want:

if (arg.weight < 0)
sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1;
else
sweight = arg.weight * DEC_DIGITS / 2 + 1;

For DEC_DIGITS == 4, they are both the same, and become (arg.weight * 2 + 1).
But when DEC_DIGITS != 4 && arg.weight >= 0, it's an improvement,
as we then don't need to generate unnecessarily many sig. digits,
and more often get exactly NUMERIC_MIN_SIG_DIGITS in the result,
while still never getting fewer than NUMERIC_MIN_SIG_DIGITS.

When DEC_DIGITS == 4, the compiler optimizes away the if/else,
and compiles it to exactly the same code as the
current sweight formula, tested with Godbolt:

Test code:
#define DEC_DIGITS 4
int sweight(weight) {
if (weight < 0)
return (weight + 1) * DEC_DIGITS / 2 - 1;
else
return weight * DEC_DIGITS / 2 + 1;
}

Output for x86-64 gcc (trunk):

sweight:
lea eax, [rdi+1+rdi]
ret

So, the extra if/else shouldn't cause any overhead, when DEC_DIGITS == 4.

Not sure how/if it can be mathematically proven why it's more correct
when DEC_DIGITS != 4, i.e., when it makes a difference.
I derived it based on the insight that the square root weight
should naturally be half the arg weight, and that the integer divison
means we must adjust for when the weight is not evenly divisable by two.

But it seems possible to gain some confidence about the correctness of the
improvement by experimentally testing a wide range of negative/positive
arg.weight.

I wrote the attached script, test_sweight_formulas.sh, for that purpose.
It compares the number of sig. digits in the result for 

sqrt(trim_scale(2::numeric*10::numeric^exp))

where exp is -40..40, that is, for args between
0.0002
0.002
...
2000
2

The last column shows the diff in number of sig. figs between HEAD and 
fix-sweight-v2.

As expected, there is no difference for DEC_DIGITS == 4.
But note the differences for DEC_DIGITS == 2 and DEC_DIGITS == 1 further down.

--
-- Comparison of sig_figs(sqrt(n)) for HEAD vs fix-sweight-v2
-- when DEC_DIGITS == 4
--
 DEC_DIGITS |  n   | HEAD | fix-sweight-v2 | diff
+--+--++--
  4 | 2e-40|   21 | 21 |0
  4 | 2e-39|   20 | 20 |0
  4 | 2e-38|   20 | 20 |0
  4 | 2e-37|   19 | 19 |0
  4 | 2e-36|   19 | 19 |0
  4 | 2e-35|   18 | 18 |0
  4 | 2e-34|   18 | 18 |0
  4 | 2e-33|   17 | 17 |0
  4 | 2e-32|   17 | 17 |0
  4 | 2e-31|   16 | 16 |0
  4 | 2e-30|   17 | 17 |0
  4 | 2e-29|   17 | 17 |0
  4 | 2e-28|   16 | 16 |0
  4 | 2e-27|   16 | 16 |0
  4 | 2e-26|   17 | 17 |0
  4 | 2e-25|   17 | 17 |0
  4 | 2e-24|   16 | 16 |0
  4 | 2e-23|   16 | 16 |0
  4 | 2e-22|   17 | 17 |0
  4 | 2e-21|   17 | 17 |0
  4 | 2e-20|   16 | 16 |0
  4 | 2e-19|   16 | 16 |0
  4 | 2e-18|   17 | 17 |0
  4 | 2e-17|   17 | 17 |0
  4 | 2e-16|   16 | 16 |0
  4 | 2e-15|   16 | 16 |0
  4 | 2e-14|   17 | 17 |0
  4 | 2e-13|   17 | 17 |0
  4 | 2e-12|   16 | 16 |0
  4 | 2e-11|   16 | 16 |0
  4 | 0.02 |   17 | 17 |0
  4 | 0.2  |   17 | 17 |0
  4 | 0.0002   |   16 | 16 |0
  4 | 0.002|   16 | 16 |0
  4 | 

Re: [PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-29 Thread Dean Rasheed
On Sat, 28 Jan 2023 at 22:14, Joel Jacobson  wrote:
>
> I found what appears to be a small harmless error in numeric.c,
> that seems worthwhile to fix only because it's currently causes confusion.
>

Shrug. Looking at git blame, it's been like that for about 20 years,
and I wasn't aware of it causing confusion.

> HEAD, patched:
> sweight = (arg.weight * DEC_DIGITS) / 2 + 1
>

You haven't actually said why this formula is more correct than the
current one. I believe that it is when arg.weight >= 0, but I'm not
convinced it's correct for arg.weight < 0.

Given that this is only an approximate computation, which ensures
roughly 16 significant digits in the result, but can't guarantee
exactly 16 digits, I'm not convinced of the benefits of changing it.

> Note, however, that it's still possible to find examples of when sqrt(numeric)
> produce results with different precision for different DEC_DIGITS/NBASE 
> values,
> but in such cases, it's intentional, and due to getting additional precision
> for free, since the larger the NBASE, the more decimal digits are produced
> at the same time per iteration in the calculation.
>
> Example:
>
> HEAD, unpatched
> DEC_DIGITS  sqrt(102::numeric)
> 4   10.09950493836208
> 2   10.099504938362078
> 1   10.0995049383620780
>
> HEAD, patched:
> DEC_DIGITS  sqrt(102::numeric)
> 4   10.099504938362078
> 2   10.09950493836208
> 1   10.09950493836208
>
> According to the comment in numeric_sqrt(), the goal is to give at least
> NUMERIC_MIN_SIG_DIGITS (16) significant digits.
>
> Since 10.09950493836208 has 16 significant digits, we can see above how
> DEC_DIGITS==2 causes an additional unnecessary significant digit to be 
> computed,
> and for DEC_DIGITS==1, two additional unnecessary significant digits are
> computed.
>
> The patched version returns 16 significant digits as expected for 
> DEC_DIGITS==2
> and DEC_DIGITS==1, and for DEC_DIGITS==4 we get an additional digit for free.
>

You lost me here. In unpatched HEAD, sqrt(102::numeric) produces
10.099504938362078, not 10.09950493836208 (with DEC_DIGITS = 4). And
wasn't your previous point that when DEC_DIGITS = 4, the new formula
is the same as the old one?

> In conclusion, the proposed patch fixes a harmless problem, but is important
> to fix, since otherwise, anyone who want to experiment with different
> DEC_DIGITS/NBASE combinations by changing the `#if 0` preprocessor values
> in the top of numeric.c will get surprising results from sqrt().
>

Anyone changing DEC_DIGITS/NBASE will find hundreds of regression test
failures due to changes in result precision all over the place (and
failures due to the overflow limit changing). I don't see why
numeric_sqrt() should be singled out for fixing.

> In passing, also add pow10[] values for DEC_DIGITS==2 and DEC_DIGITS==1,
> since otherwise it's not possible to compile such DEC_DIGITS values
> due to the assert:
>
> StaticAssertDecl(lengthof(pow10) == DEC_DIGITS, "mismatch with 
> DEC_DIGITS");
>

That might be worth doing, to ensure that the code still compiles for
other DEC_DIGITS/NBASE values. I'm not sure how useful that really is
anymore though. As the comment at the top says, it's kept mostly for
historical reasons.

Regards,
Dean




[PATCH] Fix old thinko in formula to compute sweight in numeric_sqrt().

2023-01-28 Thread Joel Jacobson
Hi,

I found what appears to be a small harmless error in numeric.c,
that seems worthwhile to fix only because it's currently causes confusion.

It hasn't caused any problems, since the incorrect formula happens to
always produce the same result for DEC_DIGITS==4.

However, for other DEC_DIGITS values, it causes an undesired variation in the
precision of the results returned by sqrt().

To understand the problem, let's look at the equivalent formula for sweight,
when replacing DEC_DIGITS with the values 1, 2, 4:

HEAD, unpatched:
sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1
Rewritten:
(arg.weight + 1) * 1 / 2 - 1 <=> arg.weight / 2 - 1 / 2
(arg.weight + 1) * 2 / 2 - 1 <=> arg.weight
(arg.weight + 1) * 4 / 2 - 1 <=> 2 * arg.weight + 1

HEAD, patched:
sweight = (arg.weight * DEC_DIGITS) / 2 + 1
Rewritten:
(arg.weight * 1) / 2 + 1 <=> arg.weight / 2 + 1
(arg.weight * 2) / 2 + 1 <=> arg.weight + 1
(arg.weight * 4) / 2 + 1 <=> 2 * arg.weight + 1

As we can see, the equivalent formula for the patched version is arg.weight
times half the DEC_DIGITS, plus one.

The first part of the formula is the same but note how the patched version
gives a constant addition of `+ 1` regardless of the DEC_DIGITS value,
whereas the unpatched version gives strange subtractions/additions
such as `- 1 / 2` and `+ 3`.

Demonstration of the undesired result digit precision variation effect:

HEAD, unpatched:
DEC_DIGITS  sqrt(2::numeric)
4   1.414213562373095
2   1.4142135623730950
1   1.41421356237309505

HEAD, patched:
DEC_DIGITS  sqrt(2::numeric)
4   1.414213562373095
2   1.414213562373095
1   1.414213562373095

The patched version consistently returns 16 significant digits for 
sqrt(2::numeric)
when DEC_DIGITS is 1, 2 and 4, whereas the unpatched version surprisingly
gives 18 sig. digits for DEC_DIGITS==1 and 17 sig. digits for DEC_DIGITS==2.

Note, however, that it's still possible to find examples of when sqrt(numeric)
produce results with different precision for different DEC_DIGITS/NBASE values,
but in such cases, it's intentional, and due to getting additional precision
for free, since the larger the NBASE, the more decimal digits are produced
at the same time per iteration in the calculation.

Example:

HEAD, unpatched
DEC_DIGITS  sqrt(102::numeric)
4   10.09950493836208
2   10.099504938362078
1   10.0995049383620780

HEAD, patched:
DEC_DIGITS  sqrt(102::numeric)
4   10.099504938362078
2   10.09950493836208
1   10.09950493836208

According to the comment in numeric_sqrt(), the goal is to give at least
NUMERIC_MIN_SIG_DIGITS (16) significant digits.

Since 10.09950493836208 has 16 significant digits, we can see above how
DEC_DIGITS==2 causes an additional unnecessary significant digit to be computed,
and for DEC_DIGITS==1, two additional unnecessary significant digits are
computed.

The patched version returns 16 significant digits as expected for DEC_DIGITS==2
and DEC_DIGITS==1, and for DEC_DIGITS==4 we get an additional digit for free.

To see why we should get an additional digit for the DEC_DIGITS==4 case,
let's enable NUMERIC_DEBUG and look at the result:

SELECT sqrt(102::numeric);
make_result(): NUMERIC w=0 d=0 POS 0102
make_result(): NUMERIC w=0 d=15 POS 0010 0995 0493 8362 0780
sqrt

10.099504938362078
(1 row)

Since 10.099504938362 has only 14 sig. digits, we need one more NBASE digit
in the result, thus 0780 is computed, and we get an extra decimal digit for
free.

Compare this to DEC_DIGITS==2, which for the patched version correctly
returns 10.09950493836208, since the last produced NBASE digit `08`
is sufficient, i.e. with it, the result has 16 sig. decimal digits,
which is enough, since NUMERIC_MIN_SIG_DIGITS==16.

In conclusion, the proposed patch fixes a harmless problem, but is important
to fix, since otherwise, anyone who want to experiment with different
DEC_DIGITS/NBASE combinations by changing the `#if 0` preprocessor values
in the top of numeric.c will get surprising results from sqrt().

In passing, also add pow10[] values for DEC_DIGITS==2 and DEC_DIGITS==1,
since otherwise it's not possible to compile such DEC_DIGITS values
due to the assert:

StaticAssertDecl(lengthof(pow10) == DEC_DIGITS, "mismatch with DEC_DIGITS");

/Joel