From d6056f085dc0d53663433d98eb105cb5f26624e7 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 25 May 2016 15:33:15 +0100 Subject: [PATCH] Fix implementation of "e" and "g" formats for printing floating points The previous commit which "fixed" the "e" and "g" floating point formats just printed them in the same way as "f". This is wrong. This commit provides the correct formatting. Reviewed-by: Richard Levitte --- crypto/bio/b_print.c | 149 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 139 insertions(+), 10 deletions(-) diff --git a/crypto/bio/b_print.c b/crypto/bio/b_print.c index 0ae4f25af1..36400cda5e 100644 --- a/crypto/bio/b_print.c +++ b/crypto/bio/b_print.c @@ -52,7 +52,7 @@ static int fmtstr(char **, char **, size_t *, size_t *, static int fmtint(char **, char **, size_t *, size_t *, LLONG, int, int, int, int); static int fmtfp(char **, char **, size_t *, size_t *, - LDOUBLE, int, int, int); + LDOUBLE, int, int, int, int); static int doapr_outch(char **, char **, size_t *, size_t *, int); static int _dopr(char **sbuffer, char **buffer, size_t *maxlen, size_t *retlen, int *truncated, @@ -90,6 +90,11 @@ static int _dopr(char **sbuffer, char **buffer, #define DP_C_LDOUBLE 3 #define DP_C_LLONG 4 +/* Floating point formats */ +#define F_FORMAT 0 +#define E_FORMAT 1 +#define G_FORMAT 2 + /* some handy macros */ #define char_to_int(p) (p - '0') #define OSSL_MAX(p,q) ((p >= q) ? p : q) @@ -268,7 +273,7 @@ _dopr(char **sbuffer, else fvalue = va_arg(args, double); if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max, - flags)) + flags, F_FORMAT)) return 0; break; case 'E': @@ -279,7 +284,7 @@ _dopr(char **sbuffer, else fvalue = va_arg(args, double); if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max, - flags)) + flags, E_FORMAT)) return 0; break; case 'G': @@ -290,7 +295,7 @@ _dopr(char **sbuffer, else fvalue = va_arg(args, double); if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max, - flags)) + flags, G_FORMAT)) return 0; break; case 'c': @@ -542,23 +547,28 @@ static int fmtfp(char **sbuffer, char **buffer, size_t *currlen, - size_t *maxlen, LDOUBLE fvalue, int min, int max, int flags) + size_t *maxlen, LDOUBLE fvalue, int min, int max, int flags, int style) { int signvalue = 0; LDOUBLE ufvalue; + LDOUBLE tmpvalue; char iconvert[20]; char fconvert[20]; + char econvert[20]; int iplace = 0; int fplace = 0; + int eplace = 0; int padlen = 0; int zpadlen = 0; + long exp = 0; long intpart; long fracpart; long max10; + int realstyle; if (max < 0) max = 6; - ufvalue = abs_val(fvalue); + if (fvalue < 0) signvalue = '-'; else if (flags & DP_F_PLUS) @@ -566,6 +576,68 @@ fmtfp(char **sbuffer, else if (flags & DP_F_SPACE) signvalue = ' '; + /* + * G_FORMAT sometimes prints like E_FORMAT and sometimes like F_FORMAT + * depending on the number to be printed. Work out which one it is and use + * that from here on. + */ + if (style == G_FORMAT) { + if (fvalue == 0.0) { + realstyle = F_FORMAT; + } else if (fvalue < 0.0001) { + realstyle = E_FORMAT; + } else if ((max == 0 && fvalue >= 10) + || (max > 0 && fvalue >= pow_10(max))) { + realstyle = E_FORMAT; + } else { + realstyle = F_FORMAT; + } + } else { + realstyle = style; + } + + if (style != F_FORMAT) { + tmpvalue = fvalue; + /* Calculate the exponent */ + if (fvalue != 0.0) { + while (tmpvalue < 1) { + tmpvalue *= 10; + exp--; + } + while (tmpvalue > 10) { + tmpvalue /= 10; + exp++; + } + } + if (style == G_FORMAT) { + /* + * In G_FORMAT the "precision" represents significant digits. We + * always have at least 1 significant digit. + */ + if (max == 0) + max = 1; + /* Now convert significant digits to decimal places */ + if (realstyle == F_FORMAT) { + max -= (exp + 1); + if (max < 0) { + /* + * Should not happen. If we're in F_FORMAT then exp < max? + */ + return 0; + } + } else { + /* + * In E_FORMAT there is always one significant digit in front + * of the decimal point, so: + * significant digits == 1 + decimal places + */ + max--; + } + } + if (realstyle == E_FORMAT) + fvalue = tmpvalue; + } + ufvalue = abs_val(fvalue); intpart = (long)ufvalue; /* @@ -597,16 +669,51 @@ fmtfp(char **sbuffer, iconvert[iplace] = 0; /* convert fractional part */ - do { + while (fplace < max) { + if (style == G_FORMAT && fplace == 0 && (fracpart % 10) == 0) { + /* We strip trailing zeros in G_FORMAT */ + max--; + fracpart = fracpart / 10; + if (fplace < max) + continue; + break; + } fconvert[fplace++] = "0123456789"[fracpart % 10]; fracpart = (fracpart / 10); - } while (fplace < max); + } + if (fplace == sizeof fconvert) fplace--; fconvert[fplace] = 0; - /* -1 for decimal point, another -1 if we are printing a sign */ - padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + /* convert exponent part */ + if (realstyle == E_FORMAT) { + int tmpexp; + if (exp < 0) + tmpexp = -exp; + else + tmpexp = exp; + + do { + econvert[eplace++] = "0123456789"[tmpexp % 10]; + tmpexp = (tmpexp / 10); + } while (tmpexp > 0 && eplace < (int)sizeof(econvert)); + /* Exponent is huge!! Too big to print */ + if (tmpexp > 0) + return 0; + /* Add a leading 0 for single digit exponents */ + if (eplace == 1) + econvert[eplace++] = '0'; + } + + /* + * -1 for decimal point (if we have one, i.e. max > 0), + * another -1 if we are printing a sign + */ + padlen = min - iplace - max - (max > 0 ? 1 : 0) - ((signvalue) ? 1 : 0); + /* Take some off for exponent prefix "+e" and exponent */ + if (realstyle == E_FORMAT) + padlen -= 2 + eplace; zpadlen = max - fplace; if (zpadlen < 0) zpadlen = 0; @@ -660,6 +767,28 @@ fmtfp(char **sbuffer, return 0; --zpadlen; } + if (realstyle == E_FORMAT) { + char ech; + + if ((flags & DP_F_UP) == 0) + ech = 'e'; + else + ech = 'E'; + if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ech)) + return 0; + if (exp < 0) { + if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '-')) + return 0; + } else { + if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '+')) + return 0; + } + while (eplace > 0) { + if (!doapr_outch(sbuffer, buffer, currlen, maxlen, + econvert[--eplace])) + return 0; + } + } while (padlen < 0) { if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) -- 2.25.1