static NumericVar const_two =
{1, 0, NUMERIC_POS, 0, NULL, const_two_data};
-#if DEC_DIGITS == 4 || DEC_DIGITS == 2
-static NumericDigit const_ten_data[1] = {10};
-static NumericVar const_ten =
-{1, 0, NUMERIC_POS, 0, NULL, const_ten_data};
-#elif DEC_DIGITS == 1
-static NumericDigit const_ten_data[1] = {1};
-static NumericVar const_ten =
-{1, 1, NUMERIC_POS, 0, NULL, const_ten_data};
-#endif
-
#if DEC_DIGITS == 4
static NumericDigit const_zero_point_five_data[1] = {5000};
#elif DEC_DIGITS == 2
static void power_var(NumericVar *base, NumericVar *exp, NumericVar *result);
static void power_var_int(NumericVar *base, int exp, NumericVar *result,
int rscale);
+static void power_ten_int(int exp, NumericVar *result);
static int cmp_abs(NumericVar *var1, NumericVar *var2);
static int cmp_abs_common(const NumericDigit *var1digits, int var1ndigits,
get_str_from_var_sci(NumericVar *var, int rscale)
{
int32 exponent;
- NumericVar denominator;
- NumericVar significand;
- int denom_scale;
+ NumericVar tmp_var;
size_t len;
char *str;
char *sig_out;
}
/*
- * The denominator is set to 10 raised to the power of the exponent.
- *
- * We then divide var by the denominator to get the significand, rounding
- * to rscale decimal digits in the process.
+ * Divide var by 10^exponent to get the significand, rounding to rscale
+ * decimal digits in the process.
*/
- if (exponent < 0)
- denom_scale = -exponent;
- else
- denom_scale = 0;
+ init_var(&tmp_var);
- init_var(&denominator);
- init_var(&significand);
+ power_ten_int(exponent, &tmp_var);
+ div_var(var, &tmp_var, &tmp_var, rscale, true);
+ sig_out = get_str_from_var(&tmp_var);
- power_var_int(&const_ten, exponent, &denominator, denom_scale);
- div_var(var, &denominator, &significand, rscale, true);
- sig_out = get_str_from_var(&significand);
-
- free_var(&denominator);
- free_var(&significand);
+ free_var(&tmp_var);
/*
* Allocate space for the result.
round_var(result, rscale);
}
+/*
+ * power_ten_int() -
+ *
+ * Raise ten to the power of exp, where exp is an integer. Note that unlike
+ * power_var_int(), this does no overflow/underflow checking or rounding.
+ */
+static void
+power_ten_int(int exp, NumericVar *result)
+{
+ /* Construct the result directly, starting from 10^0 = 1 */
+ set_var_from_var(&const_one, result);
+
+ /* Scale needed to represent the result exactly */
+ result->dscale = exp < 0 ? -exp : 0;
+
+ /* Base-NBASE weight of result and remaining exponent */
+ if (exp >= 0)
+ result->weight = exp / DEC_DIGITS;
+ else
+ result->weight = (exp + 1) / DEC_DIGITS - 1;
+
+ exp -= result->weight * DEC_DIGITS;
+
+ /* Final adjustment of the result's single NBASE digit */
+ while (exp-- > 0)
+ result->digits[0] *= 10;
+}
+
/* ----------------------------------------------------------------------
*
| 100
(1 row)
+-- Test scientific notation with various exponents
+WITH v(exp) AS
+ (VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
+ (1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
+SELECT exp,
+ to_char(('1.2345e'||exp)::numeric, '9.999EEEE') as numeric
+FROM v;
+ exp | numeric
+--------+----------------
+ -16379 | 1.235e-16379
+ -16378 | 1.235e-16378
+ -1234 | 1.235e-1234
+ -789 | 1.235e-789
+ -45 | 1.235e-45
+ -5 | 1.235e-05
+ -4 | 1.235e-04
+ -3 | 1.235e-03
+ -2 | 1.235e-02
+ -1 | 1.235e-01
+ 0 | 1.235e+00
+ 1 | 1.235e+01
+ 2 | 1.235e+02
+ 3 | 1.235e+03
+ 4 | 1.235e+04
+ 5 | 1.235e+05
+ 38 | 1.235e+38
+ 275 | 1.235e+275
+ 2345 | 1.235e+2345
+ 45678 | 1.235e+45678
+ 131070 | 1.235e+131070
+ 131071 | 1.235e+131071
+(22 rows)
+
-- TO_NUMBER()
--
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');
SELECT '' AS to_char_25, to_char('100'::numeric, 'FM999.');
SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
+-- Test scientific notation with various exponents
+WITH v(exp) AS
+ (VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
+ (1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
+SELECT exp,
+ to_char(('1.2345e'||exp)::numeric, '9.999EEEE') as numeric
+FROM v;
+
-- TO_NUMBER()
--
SELECT '' AS to_number_1, to_number('-34,338,492', '99G999G999');