Kaynağa Gözat

[Fix] Further fixes to printing of the FP numbers

tags/2.0
Vsevolod Stakhov 5 yıl önce
ebeveyn
işleme
8fe256d18f
3 değiştirilmiş dosya ile 189 ekleme ve 116 silme
  1. 173
    53
      contrib/fpconv/fpconv.c
  2. 3
    1
      contrib/fpconv/fpconv.h
  3. 13
    62
      src/libutil/printf.c

+ 173
- 53
contrib/fpconv/fpconv.c Dosyayı Görüntüle

#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/param.h>


#include "fpconv.h" #include "fpconv.h"
#include "powers.h" #include "powers.h"
return generate_digits (&w, &upper, &lower, digits, K); return generate_digits (&w, &upper, &lower, digits, K);
} }


static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg,
bool scientific) {
int exp = absv(K + ndigits - 1);

/* write plain integer */
if (K >= 0 && (exp < (ndigits + 7))) {
memcpy(dest, digits, ndigits);
memset(dest + ndigits, '0', K);

return ndigits + K;
}

/* write decimal w/o scientific notation */
if (!scientific || (K < 0 && (K > -7 || exp < 4))) {
int offset = ndigits - absv(K);
/* fp < 1.0 -> write leading zero */
if (offset <= 0) {
offset = -offset;
dest[0] = '0';
dest[1] = '.';

/* We have up to 21 characters in output available */
if (offset + ndigits <= 21) {
memset(dest + 2, '0', offset);
memcpy(dest + offset + 2, digits, ndigits);
static inline int emit_integer (char *digits, int ndigits,
char *dest, int K, bool neg,
unsigned precision)
{
char *d = dest;


return ndigits + 2 + offset;
}
else {
goto scientific_fallback;
}
memcpy (d, digits, ndigits);
d += ndigits;
memset (d, '0', K);
d += K;


/* fp > 1.0 */
}
else {
/* Overflow check */
if (ndigits <= 23) {
memcpy(dest, digits, offset);
dest[offset] = '.';
memcpy(dest + offset + 1, digits + offset, ndigits - offset);
return ndigits + 1;
}
precision = MIN(precision, FPCONV_BUFLEN - (ndigits + K + 1));


goto scientific_fallback;
}
if (precision) {
*d++ = '.';
memset (d, '0', precision);
d += precision;
} }


scientific_fallback:
return d - dest;
}

static inline int emit_scientific_digits (char *digits, int ndigits,
char *dest, int K, bool neg,
unsigned precision, int exp)
{
/* write decimal w/ scientific notation */ /* write decimal w/ scientific notation */
ndigits = minv(ndigits, 18 - neg); ndigits = minv(ndigits, 18 - neg);


return idx; return idx;
} }


static int filter_special (double fp, char *dest) {
static inline int emit_fixed_digits (char *digits, int ndigits,
char *dest, int K, bool neg,
unsigned precision, int exp)
{
int offset = ndigits - absv(K), to_print;
/* fp < 1.0 -> write leading zero */
if (K < 0) {
if (offset <= 0) {
if (precision) {
if (-offset >= precision) {
/* Just print 0.[0]{precision} */
dest[0] = '0';
dest[1] = '.';
memset(dest + 2, '0', precision);

return precision + 2;
}

to_print = MAX(ndigits - offset, precision);
}
else {
to_print = ndigits - offset;
}

if (to_print <= FPCONV_BUFLEN - 3) {
offset = -offset;
dest[0] = '0';
dest[1] = '.';
memset(dest + 2, '0', offset);

if (precision) {
/* The case where offset > precision is covered previously */
precision -= offset;

if (precision <= ndigits) {
/* Truncate or leave as is */
memcpy(dest + offset + 2, digits, precision);

return precision + 2 + offset;
}
else {
/* Expand */
memcpy(dest + offset + 2, digits, ndigits);
precision -= ndigits;
memset(dest + offset + 2 + ndigits, '0', precision);

return ndigits + 2 + offset + precision;
}
}
else {
memcpy(dest + offset + 2, digits, ndigits);
}

return ndigits + 2 + offset;
}
else {
return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
}
}
else {
/*
* fp > 1.0, if offset > 0 then we have less digits than
* fp exponent, so we need to switch to scientific notation to
* display number at least more or less precisely
*/
if (offset > 0 && ndigits <= FPCONV_BUFLEN - 3) {
char *d = dest;
memcpy(d, digits, offset);
d += offset;
*d++ = '.';

ndigits -= offset;

if (precision) {
if (ndigits >= precision) {
/* Truncate or leave as is */
memcpy(d, digits + offset, precision);
d += precision;
}
else {
/* Expand */
memcpy(d, digits + offset, ndigits);
precision -= ndigits;
d += ndigits;

/* Check if we have enough bufspace */
if ((d - dest) + precision <= FPCONV_BUFLEN) {
memset (d, '0', precision);
d += precision;
}
else {
memset (d, '0', FPCONV_BUFLEN - (d - dest));
d += FPCONV_BUFLEN - (d - dest);
}
}
}
else {
memcpy(d, digits + offset, ndigits);
d += ndigits;
}

return d - dest;
}
}
}

return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
}

static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg,
unsigned precision, bool scientific)
{
int exp = absv(K + ndigits - 1);

/* write plain integer */
if (K >= 0 && (exp < (ndigits + 7))) {
return emit_integer (digits, ndigits, dest, K, neg, precision);
}

/* write decimal w/o scientific notation */
if (!scientific || (K < 0 && (K > -7 || exp < 4))) {
return emit_fixed_digits (digits, ndigits, dest, K, neg, precision, exp);
}

return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
}

static int filter_special (double fp, char *dest, unsigned precision)
{
int nchars = 3; int nchars = 3;
char *d = dest;


if (fp == 0.0) { if (fp == 0.0) {
if (get_dbits (fp) & signmask) { if (get_dbits (fp) & signmask) {
dest[0] = '-';
dest[1] = '0';
return 2;
*d++ = '-';
*d++ = '0';
} }
else { else {
dest[0] = '0';
return 1;
*d++ = '0';
} }

if (precision) {
*d ++ = '.';
memset (d, '0', precision);
}

return d - dest + precision;
} }


uint64_t bits = get_dbits (fp); uint64_t bits = get_dbits (fp);
return nchars; return nchars;
} }


int fpconv_dtoa (double d, char dest[24], bool scientific) {
int
fpconv_dtoa (double d, char dest[FPCONV_BUFLEN],
unsigned precision, bool scientific)
{
char digits[18]; char digits[18];


int str_len = 0; int str_len = 0;
bool neg = false; bool neg = false;


if (precision > FPCONV_BUFLEN - 5) {
precision = FPCONV_BUFLEN - 5;
}


int spec = filter_special (d, dest + str_len);
int spec = filter_special (d, dest, precision);


if (spec) { if (spec) {
return str_len + spec;
return spec;
} }


if (get_dbits (d) & signmask) { if (get_dbits (d) & signmask) {
int K = 0; int K = 0;
int ndigits = grisu2 (d, digits, &K); int ndigits = grisu2 (d, digits, &K);


str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, scientific);
str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, precision,
scientific);


return str_len; return str_len;
} }

+ 3
- 1
contrib/fpconv/fpconv.h Dosyayı Görüntüle

#ifndef FPCONV_H #ifndef FPCONV_H
#define FPCONV_H #define FPCONV_H


#define FPCONV_BUFLEN 32
/* Fast and accurate double to string conversion based on Florian Loitsch's /* Fast and accurate double to string conversion based on Florian Loitsch's
* Grisu-algorithm[1]. * Grisu-algorithm[1].
* *
* *
*/ */


int fpconv_dtoa(double fp, char dest[32], bool scientific);
int fpconv_dtoa(double fp, char dest[FPCONV_BUFLEN], unsigned precision,
bool scientific);


#endif #endif



+ 13
- 62
src/libutil/printf.c Dosyayı Görüntüle

const gchar *fmt, const gchar *fmt,
va_list args) va_list args)
{ {
gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last, c;
gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last;
guchar c;
const gchar *buf_start = fmt, *fmt_start = NULL; const gchar *buf_start = fmt, *fmt_start = NULL;
gint d; gint d;
gdouble f; gdouble f;
} }


while (slen) { while (slen) {
hexbuf[0] = hex == 2 ? _HEX[(*p >> 4) & 0xf] :
_hex[(*p >> 4) & 0xf];
hexbuf[1] = hex == 2 ? _HEX[*p & 0xf] : _hex[*p & 0xf];
hexbuf[0] = hex == 2 ? _HEX[(*p >> 4u) & 0xfu] :
_hex[(*p >> 4u) & 0xfu];
hexbuf[1] = hex == 2 ? _HEX[*p & 0xfu] : _hex[*p & 0xfu];
RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2); RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2);
p++; p++;
slen--; slen--;


case 'f': case 'f':
f = (gdouble) va_arg (args, double); f = (gdouble) va_arg (args, double);
slen = fpconv_dtoa (f, dtoabuf, false);

if (frac_width != 0) {
const gchar *dot_pos = memchr (dtoabuf, '.', slen);

if (dot_pos) {
if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) {
/* Truncate */
slen = (dot_pos - dtoabuf) + 1 + /* xxx. */
frac_width; /* .yyy */
}
else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) {
/* Expand */
frac_width -= slen - ((dot_pos - dtoabuf) + 1);
memset (dtoabuf + slen, '0', frac_width);
slen += frac_width;
}
}
else {
/* Expand */
frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1);
dtoabuf[slen ++] = '.';
memset (dtoabuf + slen, '0', frac_width);
slen += frac_width;
}
}
slen = fpconv_dtoa (f, dtoabuf, frac_width, false);


RSPAMD_PRINTF_APPEND (dtoabuf, slen); RSPAMD_PRINTF_APPEND (dtoabuf, slen);




case 'g': case 'g':
f = (gdouble) va_arg (args, double); f = (gdouble) va_arg (args, double);
slen = fpconv_dtoa (f, dtoabuf, true);
slen = fpconv_dtoa (f, dtoabuf, 0, true);
RSPAMD_PRINTF_APPEND (dtoabuf, slen); RSPAMD_PRINTF_APPEND (dtoabuf, slen);


continue; continue;


case 'F': case 'F':
f = (gdouble) va_arg (args, long double); f = (gdouble) va_arg (args, long double);
slen = fpconv_dtoa (f, dtoabuf, false);

if (frac_width != 0) {
const gchar *dot_pos = memchr (dtoabuf, '.', slen);

if (dot_pos) {
if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) {
/* Truncate */
slen = (dot_pos - dtoabuf) + 1 + /* xxx. */
frac_width; /* .yyy */
}
else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) {
/* Expand */
frac_width -= slen - ((dot_pos - dtoabuf) + 1);
memset (dtoabuf + slen, '0', frac_width);
slen += frac_width;
}
}
else {
/* Expand */
frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1);
dtoabuf[slen ++] = '.';
memset (dtoabuf + slen, '0', frac_width);
slen += frac_width;
}
}
slen = fpconv_dtoa (f, dtoabuf, frac_width, false);


RSPAMD_PRINTF_APPEND (dtoabuf, slen); RSPAMD_PRINTF_APPEND (dtoabuf, slen);




case 'G': case 'G':
f = (gdouble) va_arg (args, long double); f = (gdouble) va_arg (args, long double);
slen = fpconv_dtoa (f, dtoabuf, true);
slen = fpconv_dtoa (f, dtoabuf, 0, true);
RSPAMD_PRINTF_APPEND (dtoabuf, slen); RSPAMD_PRINTF_APPEND (dtoabuf, slen);


continue; continue;


case 'c': case 'c':
c = va_arg (args, gint); c = va_arg (args, gint);
c &= 0xff;
c &= 0xffu;
if (G_UNLIKELY (hex)) { if (G_UNLIKELY (hex)) {
gchar hexbuf[2]; gchar hexbuf[2];
hexbuf[0] = hex == 2 ? _HEX[(c >> 4) & 0xf] :
_hex[(c >> 4) & 0xf];
hexbuf[1] = hex == 2 ? _HEX[c & 0xf] : _hex[c & 0xf];
hexbuf[0] = hex == 2 ? _HEX[(c >> 4u) & 0xfu] :
_hex[(c >> 4u) & 0xfu];
hexbuf[1] = hex == 2 ? _HEX[c & 0xfu] : _hex[c & 0xfu];


RSPAMD_PRINTF_APPEND (hexbuf, 2); RSPAMD_PRINTF_APPEND (hexbuf, 2);
} }

Loading…
İptal
Kaydet