Skip to content

Commit e61fd4a

Browse files
committed
Support EEEE (scientific notation) in to_char().
Pavel Stehule, Brendan Jurd
1 parent 933b17b commit e61fd4a

File tree

6 files changed

+347
-14
lines changed

6 files changed

+347
-14
lines changed

doc/src/sgml/func.sgml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.485 2009/08/10 16:10:19 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.486 2009/08/10 18:29:26 tgl Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -5345,7 +5345,7 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
53455345
</row>
53465346
<row>
53475347
<entry><literal>EEEE</literal></entry>
5348-
<entry>scientific notation (not implemented)</entry>
5348+
<entry>exponent for scientific notation</entry>
53495349
</row>
53505350
</tbody>
53515351
</tgroup>
@@ -5404,6 +5404,15 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
54045404
(e.g., <literal>99.9V99</literal> is not allowed).
54055405
</para>
54065406
</listitem>
5407+
5408+
<listitem>
5409+
<para>
5410+
<literal>EEEE</literal> (scientific notation) cannot be used in
5411+
combination with any of the other special formatting patterns or
5412+
modifiers, and must be at the end of the format string
5413+
(e.g., <literal>9.99EEEE</literal> is a valid pattern).
5414+
</para>
5415+
</listitem>
54075416
</itemizedlist>
54085417
</para>
54095418

@@ -5605,6 +5614,10 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
56055614
<entry><literal>to_char(12.45, '99V9')</literal></entry>
56065615
<entry><literal>'&nbsp;125'</literal></entry>
56075616
</row>
5617+
<row>
5618+
<entry><literal>to_char(0.0004859, '9.99EEEE')</literal></entry>
5619+
<entry><literal>' 4.86e-04'</literal></entry>
5620+
</row>
56085621
</tbody>
56095622
</tgroup>
56105623
</table>

src/backend/utils/adt/formatting.c

Lines changed: 178 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* -----------------------------------------------------------------------
22
* formatting.c
33
*
4-
* $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.159 2009/07/06 19:11:39 heikki Exp $
4+
* $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.160 2009/08/10 18:29:26 tgl Exp $
55
*
66
*
77
* Portions Copyright (c) 1999-2009, PostgreSQL Global Development Group
@@ -335,6 +335,7 @@ typedef struct
335335
#define NUM_F_MULTI (1 << 11)
336336
#define NUM_F_PLUS_POST (1 << 12)
337337
#define NUM_F_MINUS_POST (1 << 13)
338+
#define NUM_F_EEEE (1 << 14)
338339

339340
#define NUM_LSIGN_PRE (-1)
340341
#define NUM_LSIGN_POST 1
@@ -355,6 +356,7 @@ typedef struct
355356
#define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
356357
#define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
357358
#define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
359+
#define IS_EEEE(_f) ((_f)->flag & NUM_F_EEEE)
358360

359361
/* ----------
360362
* Format picture cache
@@ -821,7 +823,7 @@ static const KeyWord NUM_keywords[] = {
821823
{"B", 1, NUM_B}, /* B */
822824
{"C", 1, NUM_C}, /* C */
823825
{"D", 1, NUM_D}, /* D */
824-
{"E", 1, NUM_E}, /* E */
826+
{"EEEE", 4, NUM_E}, /* E */
825827
{"FM", 2, NUM_FM}, /* F */
826828
{"G", 1, NUM_G}, /* G */
827829
{"L", 1, NUM_L}, /* L */
@@ -837,7 +839,7 @@ static const KeyWord NUM_keywords[] = {
837839
{"b", 1, NUM_B}, /* b */
838840
{"c", 1, NUM_C}, /* c */
839841
{"d", 1, NUM_D}, /* d */
840-
{"e", 1, NUM_E}, /* e */
842+
{"eeee", 4, NUM_E}, /* e */
841843
{"fm", 2, NUM_FM}, /* f */
842844
{"g", 1, NUM_G}, /* g */
843845
{"l", 1, NUM_L}, /* l */
@@ -1044,6 +1046,14 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
10441046
if (n->type != NODE_TYPE_ACTION)
10451047
return;
10461048

1049+
if (IS_EEEE(num) && n->key->id != NUM_E)
1050+
{
1051+
NUM_cache_remove(last_NUMCacheEntry);
1052+
ereport(ERROR,
1053+
(errcode(ERRCODE_SYNTAX_ERROR),
1054+
errmsg("\"EEEE\" must be the last pattern used")));
1055+
}
1056+
10471057
switch (n->key->id)
10481058
{
10491059
case NUM_9:
@@ -1217,10 +1227,25 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
12171227
break;
12181228

12191229
case NUM_E:
1220-
NUM_cache_remove(last_NUMCacheEntry);
1221-
ereport(ERROR,
1222-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1223-
errmsg("\"E\" is not supported")));
1230+
if (IS_EEEE(num))
1231+
{
1232+
NUM_cache_remove(last_NUMCacheEntry);
1233+
ereport(ERROR,
1234+
(errcode(ERRCODE_SYNTAX_ERROR),
1235+
errmsg("cannot use \"EEEE\" twice")));
1236+
}
1237+
if (IS_BLANK(num) || IS_FILLMODE(num) || IS_LSIGN(num) ||
1238+
IS_BRACKET(num) || IS_MINUS(num) || IS_PLUS(num) ||
1239+
IS_ROMAN(num) || IS_MULTI(num))
1240+
{
1241+
NUM_cache_remove(last_NUMCacheEntry);
1242+
ereport(ERROR,
1243+
(errcode(ERRCODE_SYNTAX_ERROR),
1244+
errmsg("\"EEEE\" is incompatible with other formats"),
1245+
errdetail("\"EEEE\" may only be used together with digit and decimal point patterns.")));
1246+
}
1247+
num->flag |= NUM_F_EEEE;
1248+
break;
12241249
}
12251250

12261251
return;
@@ -4145,6 +4170,15 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41454170
if (Np->Num->zero_start)
41464171
--Np->Num->zero_start;
41474172

4173+
if (IS_EEEE(Np->Num))
4174+
{
4175+
if (!Np->is_to_char)
4176+
ereport(ERROR,
4177+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4178+
errmsg("\"EEEE\" not supported for input")));
4179+
return strcpy(inout, number);
4180+
}
4181+
41484182
/*
41494183
* Roman correction
41504184
*/
@@ -4153,7 +4187,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41534187
if (!Np->is_to_char)
41544188
ereport(ERROR,
41554189
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4156-
errmsg("\"RN\" not supported")));
4190+
errmsg("\"RN\" not supported for input")));
41574191

41584192
Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
41594193
Np->Num->pre = Np->num_pre = Np->sign = 0;
@@ -4240,7 +4274,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
42404274

42414275
#ifdef DEBUG_TO_FROM_CHAR
42424276
elog(DEBUG_elog_output,
4243-
"\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s",
4277+
"\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s\n\tEEEE: %s",
42444278
Np->sign,
42454279
Np->number,
42464280
Np->Num->pre,
@@ -4256,7 +4290,8 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
42564290
IS_PLUS(Np->Num) ? "Yes" : "No",
42574291
IS_MINUS(Np->Num) ? "Yes" : "No",
42584292
IS_FILLMODE(Np->Num) ? "Yes" : "No",
4259-
IS_ROMAN(Np->Num) ? "Yes" : "No"
4293+
IS_ROMAN(Np->Num) ? "Yes" : "No",
4294+
IS_EEEE(Np->Num) ? "Yes" : "No"
42604295
);
42614296
#endif
42624297

@@ -4626,6 +4661,39 @@ numeric_to_char(PG_FUNCTION_ARGS)
46264661
int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
46274662
NumericGetDatum(x))));
46284663
}
4664+
else if (IS_EEEE(&Num))
4665+
{
4666+
orgnum = numeric_out_sci(value, Num.post);
4667+
4668+
/*
4669+
* numeric_out_sci() does not emit a sign for positive numbers. We
4670+
* need to add a space in this case so that positive and negative
4671+
* numbers are aligned. We also have to do the right thing for NaN.
4672+
*/
4673+
if (strcmp(orgnum, "NaN") == 0)
4674+
{
4675+
/*
4676+
* Allow 6 characters for the leading sign, the decimal point, "e",
4677+
* the exponent's sign and two exponent digits.
4678+
*/
4679+
numstr = (char *) palloc(Num.pre + Num.post + 7);
4680+
fill_str(numstr, '#', Num.pre + Num.post + 6);
4681+
*numstr = ' ';
4682+
*(numstr + Num.pre + 1) = '.';
4683+
}
4684+
else if (*orgnum != '-')
4685+
{
4686+
numstr = (char *) palloc(strlen(orgnum) + 2);
4687+
*numstr = ' ';
4688+
strcpy(numstr + 1, orgnum);
4689+
len = strlen(numstr);
4690+
}
4691+
else
4692+
{
4693+
numstr = orgnum;
4694+
len = strlen(orgnum);
4695+
}
4696+
}
46294697
else
46304698
{
46314699
Numeric val = value;
@@ -4707,6 +4775,23 @@ int4_to_char(PG_FUNCTION_ARGS)
47074775
*/
47084776
if (IS_ROMAN(&Num))
47094777
numstr = orgnum = int_to_roman(value);
4778+
else if (IS_EEEE(&Num))
4779+
{
4780+
/* we can do it easily because float8 won't lose any precision */
4781+
float8 val = (float8) value;
4782+
4783+
orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4784+
snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, val);
4785+
4786+
/*
4787+
* Swap a leading positive sign for a space.
4788+
*/
4789+
if (*orgnum == '+')
4790+
*orgnum = ' ';
4791+
4792+
len = strlen(orgnum);
4793+
numstr = orgnum;
4794+
}
47104795
else
47114796
{
47124797
if (IS_MULTI(&Num))
@@ -4785,6 +4870,33 @@ int8_to_char(PG_FUNCTION_ARGS)
47854870
numstr = orgnum = int_to_roman(DatumGetInt32(
47864871
DirectFunctionCall1(int84, Int64GetDatum(value))));
47874872
}
4873+
else if (IS_EEEE(&Num))
4874+
{
4875+
/* to avoid loss of precision, must go via numeric not float8 */
4876+
Numeric val;
4877+
4878+
val = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
4879+
Int64GetDatum(value)));
4880+
orgnum = numeric_out_sci(val, Num.post);
4881+
4882+
/*
4883+
* numeric_out_sci() does not emit a sign for positive numbers. We
4884+
* need to add a space in this case so that positive and negative
4885+
* numbers are aligned. We don't have to worry about NaN here.
4886+
*/
4887+
if (*orgnum != '-')
4888+
{
4889+
numstr = (char *) palloc(strlen(orgnum) + 2);
4890+
*numstr = ' ';
4891+
strcpy(numstr + 1, orgnum);
4892+
len = strlen(numstr);
4893+
}
4894+
else
4895+
{
4896+
numstr = orgnum;
4897+
len = strlen(orgnum);
4898+
}
4899+
}
47884900
else
47894901
{
47904902
if (IS_MULTI(&Num))
@@ -4859,6 +4971,34 @@ float4_to_char(PG_FUNCTION_ARGS)
48594971

48604972
if (IS_ROMAN(&Num))
48614973
numstr = orgnum = int_to_roman((int) rint(value));
4974+
else if (IS_EEEE(&Num))
4975+
{
4976+
numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
4977+
if (isnan(value) || is_infinite(value))
4978+
{
4979+
/*
4980+
* Allow 6 characters for the leading sign, the decimal point, "e",
4981+
* the exponent's sign and two exponent digits.
4982+
*/
4983+
numstr = (char *) palloc(Num.pre + Num.post + 7);
4984+
fill_str(numstr, '#', Num.pre + Num.post + 6);
4985+
*numstr = ' ';
4986+
*(numstr + Num.pre + 1) = '.';
4987+
}
4988+
else
4989+
{
4990+
snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
4991+
4992+
/*
4993+
* Swap a leading positive sign for a space.
4994+
*/
4995+
if (*orgnum == '+')
4996+
*orgnum = ' ';
4997+
4998+
len = strlen(orgnum);
4999+
numstr = orgnum;
5000+
}
5001+
}
48625002
else
48635003
{
48645004
float4 val = value;
@@ -4935,6 +5075,34 @@ float8_to_char(PG_FUNCTION_ARGS)
49355075

49365076
if (IS_ROMAN(&Num))
49375077
numstr = orgnum = int_to_roman((int) rint(value));
5078+
else if (IS_EEEE(&Num))
5079+
{
5080+
numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
5081+
if (isnan(value) || is_infinite(value))
5082+
{
5083+
/*
5084+
* Allow 6 characters for the leading sign, the decimal point, "e",
5085+
* the exponent's sign and two exponent digits.
5086+
*/
5087+
numstr = (char *) palloc(Num.pre + Num.post + 7);
5088+
fill_str(numstr, '#', Num.pre + Num.post + 6);
5089+
*numstr = ' ';
5090+
*(numstr + Num.pre + 1) = '.';
5091+
}
5092+
else
5093+
{
5094+
snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
5095+
5096+
/*
5097+
* Swap a leading positive sign for a space.
5098+
*/
5099+
if (*orgnum == '+')
5100+
*orgnum = ' ';
5101+
5102+
len = strlen(orgnum);
5103+
numstr = orgnum;
5104+
}
5105+
}
49385106
else
49395107
{
49405108
float8 val = value;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy