Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6eb3eb5

Browse files
committed
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,and invoked psprintf only for ordinary float values. This was done becauseplatform implementations of snprintf produce varying representations ofthese special cases. But now that we use snprintf.c always, it's betterto give it the responsibility to produce a uniform representation ofthese cases, so that we have uniformity across the board not only infloat4out/float8out. Hence, move that work into fmtfloat().Also, teach fmtfloat() to recognize IEEE minus zero and handle itcorrectly. The previous coding worked only accidentally, and wouldfail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we'reusing snprintf.c everywhere, it's not acceptable for it to do weirdthings in corner cases. (This incidentally avoids a portabilityproblem we've seen on some really ancient platforms, that nativesprintf does the wrong thing with minus zero.)Also, introduce a new entry point in snprintf.c to allow float[48]outto bypass the work of interpreting a well-known format spec, as wellas bypassing the overhead of the psprintf layer. I modeled this APIloosely on strfromd(). In my testing, this brings float[48]out backto approximately the same speed they had when using native snprintf,fixing one of the main performance issues caused by using snprintf.c.(There is some talk of more aggressive work to improve the speed offloating-point output conversion, but these changes seem to providea better starting point for such work anyway.)Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()allows removing <ctype.h> from snprintf.c's #includes. I also removeda few other #includes that I think are historical, though the buildfarmmay expose that as wrong.Discussion:https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
1 parentf9eb7c1 commit6eb3eb5

File tree

3 files changed

+145
-74
lines changed

3 files changed

+145
-74
lines changed

‎src/backend/utils/adt/float.c

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -243,30 +243,10 @@ Datum
243243
float4out(PG_FUNCTION_ARGS)
244244
{
245245
float4num=PG_GETARG_FLOAT4(0);
246-
char*ascii;
247-
248-
if (isnan(num))
249-
PG_RETURN_CSTRING(pstrdup("NaN"));
250-
251-
switch (is_infinite(num))
252-
{
253-
case1:
254-
ascii=pstrdup("Infinity");
255-
break;
256-
case-1:
257-
ascii=pstrdup("-Infinity");
258-
break;
259-
default:
260-
{
261-
intndig=FLT_DIG+extra_float_digits;
262-
263-
if (ndig<1)
264-
ndig=1;
265-
266-
ascii=psprintf("%.*g",ndig,num);
267-
}
268-
}
246+
char*ascii= (char*)palloc(32);
247+
intndig=FLT_DIG+extra_float_digits;
269248

249+
(void)pg_strfromd(ascii,32,ndig,num);
270250
PG_RETURN_CSTRING(ascii);
271251
}
272252

@@ -479,30 +459,10 @@ float8out(PG_FUNCTION_ARGS)
479459
char*
480460
float8out_internal(doublenum)
481461
{
482-
char*ascii;
483-
484-
if (isnan(num))
485-
returnpstrdup("NaN");
486-
487-
switch (is_infinite(num))
488-
{
489-
case1:
490-
ascii=pstrdup("Infinity");
491-
break;
492-
case-1:
493-
ascii=pstrdup("-Infinity");
494-
break;
495-
default:
496-
{
497-
intndig=DBL_DIG+extra_float_digits;
498-
499-
if (ndig<1)
500-
ndig=1;
501-
502-
ascii=psprintf("%.*g",ndig,num);
503-
}
504-
}
462+
char*ascii= (char*)palloc(32);
463+
intndig=DBL_DIG+extra_float_digits;
505464

465+
(void)pg_strfromd(ascii,32,ndig,num);
506466
returnascii;
507467
}
508468

‎src/include/port.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ extern intpg_printf(const char *fmt,...) pg_attribute_printf(1, 2);
187187
#definefprintfpg_fprintf
188188
#defineprintf(...)pg_printf(__VA_ARGS__)
189189

190+
/* This is also provided by snprintf.c */
191+
externintpg_strfromd(char*str,size_tcount,intprecision,doublevalue);
192+
190193
/* Replace strerror() with our own, somewhat more robust wrapper */
191194
externchar*pg_strerror(interrnum);
192195
#definestrerror pg_strerror

‎src/port/snprintf.c

Lines changed: 136 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,7 @@
3333

3434
#include"c.h"
3535

36-
#include<ctype.h>
37-
#include<limits.h>
3836
#include<math.h>
39-
#ifndefWIN32
40-
#include<sys/ioctl.h>
41-
#endif
42-
#include<sys/param.h>
4337

4438
/*
4539
* We used to use the platform's NL_ARGMAX here, but that's a bad idea,
@@ -1111,10 +1105,6 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
11111105
intzeropadlen=0;/* amount to pad with zeroes */
11121106
intpadlen;/* amount to pad with spaces */
11131107

1114-
/* Handle sign (NaNs have no sign) */
1115-
if (!isnan(value)&&adjust_sign((value<0),forcesign,&signvalue))
1116-
value=-value;
1117-
11181108
/*
11191109
* We rely on the regular C library's sprintf to do the basic conversion,
11201110
* then handle padding considerations here.
@@ -1128,34 +1118,62 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
11281118
* bytes and limit requested precision to 350 digits; this should prevent
11291119
* buffer overrun even with non-IEEE math. If the original precision
11301120
* request was more than 350, separately pad with zeroes.
1121+
*
1122+
* We handle infinities and NaNs specially to ensure platform-independent
1123+
* output.
11311124
*/
11321125
if (precision<0)/* cover possible overflow of "accum" */
11331126
precision=0;
11341127
prec=Min(precision,350);
11351128

1136-
if (pointflag)
1129+
if (isnan(value))
11371130
{
1138-
zeropadlen=precision-prec;
1139-
fmt[0]='%';
1140-
fmt[1]='.';
1141-
fmt[2]='*';
1142-
fmt[3]=type;
1143-
fmt[4]='\0';
1144-
vallen=sprintf(convert,fmt,prec,value);
1131+
strcpy(convert,"NaN");
1132+
vallen=3;
1133+
/* no zero padding, regardless of precision spec */
11451134
}
11461135
else
11471136
{
1148-
fmt[0]='%';
1149-
fmt[1]=type;
1150-
fmt[2]='\0';
1151-
vallen=sprintf(convert,fmt,value);
1152-
}
1153-
if (vallen<0)
1154-
gotofail;
1137+
/*
1138+
* Handle sign (NaNs have no sign, so we don't do this in the case
1139+
* above). "value < 0.0" will not be true for IEEE minus zero, so we
1140+
* detect that by looking for the case where value equals 0.0
1141+
* according to == but not according to memcmp.
1142+
*/
1143+
staticconstdoubledzero=0.0;
1144+
1145+
if (adjust_sign((value<0.0||
1146+
(value==0.0&&
1147+
memcmp(&value,&dzero,sizeof(double))!=0)),
1148+
forcesign,&signvalue))
1149+
value=-value;
11551150

1156-
/* If it's infinity or NaN, forget about doing any zero-padding */
1157-
if (zeropadlen>0&& !isdigit((unsignedchar)convert[vallen-1]))
1158-
zeropadlen=0;
1151+
if (isinf(value))
1152+
{
1153+
strcpy(convert,"Infinity");
1154+
vallen=8;
1155+
/* no zero padding, regardless of precision spec */
1156+
}
1157+
elseif (pointflag)
1158+
{
1159+
zeropadlen=precision-prec;
1160+
fmt[0]='%';
1161+
fmt[1]='.';
1162+
fmt[2]='*';
1163+
fmt[3]=type;
1164+
fmt[4]='\0';
1165+
vallen=sprintf(convert,fmt,prec,value);
1166+
}
1167+
else
1168+
{
1169+
fmt[0]='%';
1170+
fmt[1]=type;
1171+
fmt[2]='\0';
1172+
vallen=sprintf(convert,fmt,value);
1173+
}
1174+
if (vallen<0)
1175+
gotofail;
1176+
}
11591177

11601178
padlen=compute_padlen(minlen,vallen+zeropadlen,leftjust);
11611179

@@ -1197,6 +1215,96 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
11971215
target->failed= true;
11981216
}
11991217

1218+
/*
1219+
* Nonstandard entry point to print a double value efficiently.
1220+
*
1221+
* This is approximately equivalent to strfromd(), but has an API more
1222+
* adapted to what float8out() wants. The behavior is like snprintf()
1223+
* with a format of "%.ng", where n is the specified precision.
1224+
* However, the target buffer must be nonempty (i.e. count > 0), and
1225+
* the precision is silently bounded to a sane range.
1226+
*/
1227+
int
1228+
pg_strfromd(char*str,size_tcount,intprecision,doublevalue)
1229+
{
1230+
PrintfTargettarget;
1231+
intsignvalue=0;
1232+
intvallen;
1233+
charfmt[8];
1234+
charconvert[64];
1235+
1236+
/* Set up the target like pg_snprintf, but require nonempty buffer */
1237+
Assert(count>0);
1238+
target.bufstart=target.bufptr=str;
1239+
target.bufend=str+count-1;
1240+
target.stream=NULL;
1241+
target.nchars=0;
1242+
target.failed= false;
1243+
1244+
/*
1245+
* We bound precision to a reasonable range; the combination of this and
1246+
* the knowledge that we're using "g" format without padding allows the
1247+
* convert[] buffer to be reasonably small.
1248+
*/
1249+
if (precision<1)
1250+
precision=1;
1251+
elseif (precision>32)
1252+
precision=32;
1253+
1254+
/*
1255+
* The rest is just an inlined version of the fmtfloat() logic above,
1256+
* simplified using the knowledge that no padding is wanted.
1257+
*/
1258+
if (isnan(value))
1259+
{
1260+
strcpy(convert,"NaN");
1261+
vallen=3;
1262+
}
1263+
else
1264+
{
1265+
staticconstdoubledzero=0.0;
1266+
1267+
if (value<0.0||
1268+
(value==0.0&&
1269+
memcmp(&value,&dzero,sizeof(double))!=0))
1270+
{
1271+
signvalue='-';
1272+
value=-value;
1273+
}
1274+
1275+
if (isinf(value))
1276+
{
1277+
strcpy(convert,"Infinity");
1278+
vallen=8;
1279+
}
1280+
else
1281+
{
1282+
fmt[0]='%';
1283+
fmt[1]='.';
1284+
fmt[2]='*';
1285+
fmt[3]='g';
1286+
fmt[4]='\0';
1287+
vallen=sprintf(convert,fmt,precision,value);
1288+
if (vallen<0)
1289+
{
1290+
target.failed= true;
1291+
gotofail;
1292+
}
1293+
}
1294+
}
1295+
1296+
if (signvalue)
1297+
dopr_outch(signvalue,&target);
1298+
1299+
dostr(convert,vallen,&target);
1300+
1301+
fail:
1302+
*(target.bufptr)='\0';
1303+
returntarget.failed ?-1 : (target.bufptr-target.bufstart
1304+
+target.nchars);
1305+
}
1306+
1307+
12001308
staticvoid
12011309
dostr(constchar*str,intslen,PrintfTarget*target)
12021310
{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp