STORE_LC_NUMERIC_SET_TO_NEEDED();
local_radix = PL_numeric_local && PL_numeric_radix_sv;
- if (local_radix && SvLEN(PL_numeric_radix_sv) > 1) {
- size += SvLEN(PL_numeric_radix_sv) - 1;
+ if (local_radix && SvCUR(PL_numeric_radix_sv) > 1) {
+ size += SvCUR(PL_numeric_radix_sv) - 1;
s = SvGROW_mutable(sv, size);
}
return var;
}
+/* Implement a fast "%.0f": given a pointer to the end of a buffer (caller
+ * ensures it's big enough), back fill it with the rounded integer part of
+ * nv. Returns ptr to start of string, and sets *len to its length.
+ * Returns NULL if not convertible.
+ */
+
STATIC char *
S_F0convert(NV nv, char *const endbuf, STRLEN *const len)
{
PERL_ARGS_ASSERT_F0CONVERT;
- if (UNLIKELY(Perl_isinfnan(nv))) {
- STRLEN n = S_infnan_2pv(nv, endbuf - *len, *len, 0);
- *len = n;
- return endbuf - n;
- }
+ assert(!Perl_isinfnan(nv));
if (neg)
nv = -nv;
if (nv < UV_MAX) {
}
#if !defined(USE_LONG_DOUBLE) && !defined(USE_QUADMATH)
- /* special-case "%.<number>[gf]" */
+ /* special-case "%.0f" and "%.<number>g" */
if ( !args && patlen <= 5 && pat[0] == '%' && pat[1] == '.'
&& (pat[patlen-1] == 'g' || pat[patlen-1] == 'f') ) {
unsigned digits = 0;
if (digits && digits < sizeof(ebuf) - NV_DIG - 10) {
/* 0, point, slack */
STORE_LC_NUMERIC_SET_TO_NEEDED();
- SNPRINTF_G(nv, ebuf, size, digits);
+ SNPRINTF_G(nv, ebuf, sizeof(ebuf), digits);
sv_catpv_nomg(sv, ebuf);
if (*ebuf) /* May return an empty string for digits==0 */
return;
bool is_utf8 = FALSE; /* is this item utf8? */
bool used_explicit_ix = FALSE;
bool arg_missing = FALSE;
-#ifdef HAS_LDBL_SPRINTF_BUG
- /* This is to try to fix a bug with irix/nonstop-ux/powerux and
- with sfio - Allen <allens@cpan.org> */
- bool fix_ldbl_sprintf_bug = FALSE;
-#endif
-
char esignbuf[4];
U8 utf8buf[UTF8_MAXBYTES+1];
STRLEN esignlen = 0;
unsigned base = 0;
IV iv = 0;
UV uv = 0;
+ bool is_simple = TRUE; /* no fancy qualifiers */
+ STRLEN radix_len; /* SvCUR(PL_numeric_radix_sv) */
+
/* We need a long double target in case HAS_LONG_DOUBLE,
* even without USE_LONG_DOUBLE, so that we can printf with
* long double formats, even without NV being long double.
NV_TO_FV(nv, fv);
}
- float_need = 0;
+ /* special-case "%.0f" */
+ is_simple = ( !(width || left || plus || alt)
+ && fill != '0'
+ && has_precis
+ && intsize != 'q');
+
+ if (is_simple && c == 'f' && !precis && !Perl_isinfnan(nv)) {
+ if ((eptr = F0convert(nv, ebuf + sizeof ebuf, &elen)))
+ break;
+ }
+
+ /*determine the radix point len, e.g. length(".") in "1.2" */
+ radix_len = 1; /* assume '.' */
+#ifdef USE_LOCALE_NUMERIC
+ /* note that we may either explicitly use PL_numeric_radix_sv
+ * below, or implicitly, via an snprintf() variant.
+ * Note also things like ps_AF.utf8 which has
+ * "\N{ARABIC DECIMAL SEPARATOR} as a radix point */
+ STORE_LC_NUMERIC_SET_TO_NEEDED();
+ if (PL_numeric_radix_sv && IN_LC(LC_NUMERIC)) {
+ radix_len = SvCUR(PL_numeric_radix_sv);
+ /* note that this will convert the output to utf8 even if
+ * if the radix point didn't get output */
+ is_utf8 = SvUTF8(PL_numeric_radix_sv);
+ }
+ RESTORE_LC_NUMERIC();
+#endif
+ /* most basic: space for "Inf"/"Nan" and \0, plus the "." */
+ float_need = 4 + radix_len;
+
/* frexp() (or frexpl) has some unspecified behaviour for
* nan/inf/-inf, so let's avoid calling that on non-finites. */
if (isALPHA_FOLD_NE(c, 'e') && FV_ISFINITE(fv)) {
float_need += (DOUBLEDOUBLE_MAXBITS/8 + 1) * 2;
/* the size for the exponent already added */
#endif
-#ifdef USE_LOCALE_NUMERIC
- STORE_LC_NUMERIC_SET_TO_NEEDED();
- if (PL_numeric_radix_sv && IN_LC(LC_NUMERIC))
- float_need += SvLEN(PL_numeric_radix_sv);
- RESTORE_LC_NUMERIC();
-#endif
}
else if (i > 0) {
float_need = BIT_DIGITS(i);
if ((intsize == 'q') && (c == 'f') &&
((fv < MY_DBL_MAX_BUG) && (fv > -MY_DBL_MAX_BUG)) &&
- (float_need < DBL_DIG)) {
+ (float_need < DBL_DIG))
+ {
+ bool fix_ldbl_sprintf_bug = FALSE;
+
/* it's going to be short enough that
* long double precision is not needed */
fix_ldbl_sprintf_bug = TRUE;
}
}
+
if (fix_ldbl_sprintf_bug == TRUE) {
double temp;
PL_efloatbuf[0] = '\0';
}
- if ( !(width || left || plus || alt) && fill != '0'
- && has_precis && intsize != 'q' /* Shortcuts */
- && LIKELY(!Perl_isinfnan((NV)fv)) ) {
+ /* special-case "%.<number>g" */
+ if (is_simple && LIKELY(!Perl_isinfnan((NV)fv)) ) {
/* See earlier comment about buggy Gconvert when digits,
aka precis is 0 */
if ( c == 'g' && precis ) {
elen = strlen(PL_efloatbuf);
goto float_converted;
}
- } else if ( c == 'f' && !precis ) {
- if ((eptr = F0convert(nv, ebuf + sizeof ebuf, &elen)))
- break;
- }
+ }
}
if (UNLIKELY(hexfp)) {
eptr = PL_efloatbuf;
assert((IV)elen > 0); /* here zero elen is bad */
-#ifdef USE_LOCALE_NUMERIC
- /* If the decimal point character in the string is UTF-8, make the
- * output utf8 */
- if (PL_numeric_radix_sv && SvUTF8(PL_numeric_radix_sv)
- && instr(eptr, SvPVX_const(PL_numeric_radix_sv)))
- {
- is_utf8 = TRUE;
- }
-#endif
break;