mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-05 01:48:07 +02:00
Add %g conversion specifer to output-stream for limited precision
The %g conversion specifier is for printing numbers that were at some time stored in a cairo_fixed_t type and as a result have their precision limited by the size of CAIRO_FIXED_FRAC_BITS. Using %g will limit the number of digits after the decimal point to the minimum required to preserve the available precision.
This commit is contained in:
parent
f3734085a1
commit
d78013470b
1 changed files with 56 additions and 31 deletions
|
|
@ -44,6 +44,26 @@
|
|||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Numbers printed with %f are printed with this number of significant
|
||||
* digits after the decimal.
|
||||
*/
|
||||
#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
|
||||
|
||||
/* Numbers printed with %g are assumed to only have CAIRO_FIXED_FRAC_BITS
|
||||
* bits of precision available after the decimal point.
|
||||
*
|
||||
* FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal
|
||||
* digits after the decimal point required to preserve the available
|
||||
* precision.
|
||||
*
|
||||
* The conversion is:
|
||||
*
|
||||
* FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) )
|
||||
*
|
||||
* We can replace ceil(x) with (int)(x+1) since x will never be an
|
||||
* integer for any likely value of CAIRO_FIXED_FRAC_BITS.
|
||||
*/
|
||||
#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1))
|
||||
|
||||
void
|
||||
_cairo_output_stream_init (cairo_output_stream_t *stream,
|
||||
|
|
@ -236,8 +256,6 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
|
|||
}
|
||||
}
|
||||
|
||||
#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
|
||||
|
||||
/* Format a double in a locale independent way and trim trailing
|
||||
* zeros. Based on code from Alex Larson <alexl@redhat.com>.
|
||||
* http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
|
||||
|
|
@ -247,7 +265,7 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
|
|||
* into cairo (see COPYING). -- Kristian Høgsberg <krh@redhat.com>
|
||||
*/
|
||||
static void
|
||||
_cairo_dtostr (char *buffer, size_t size, double d)
|
||||
_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision)
|
||||
{
|
||||
struct lconv *locale_data;
|
||||
const char *decimal_point;
|
||||
|
|
@ -266,40 +284,44 @@ _cairo_dtostr (char *buffer, size_t size, double d)
|
|||
|
||||
assert (decimal_point_len != 0);
|
||||
|
||||
/* Using "%f" to print numbers less than 0.1 will result in
|
||||
* reduced precision due to the default 6 digits after the
|
||||
* decimal point.
|
||||
*
|
||||
* For numbers is < 0.1, we print with maximum precision and count
|
||||
* the number of zeros between the decimal point and the first
|
||||
* significant digit. We then print the number again with the
|
||||
* number of decimal places that gives us the required number of
|
||||
* significant digits. This ensures the number is correctly
|
||||
* rounded.
|
||||
*/
|
||||
if (fabs (d) >= 0.1) {
|
||||
snprintf (buffer, size, "%f", d);
|
||||
if (limited_precision) {
|
||||
snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d);
|
||||
} else {
|
||||
snprintf (buffer, size, "%.18f", d);
|
||||
p = buffer;
|
||||
/* Using "%f" to print numbers less than 0.1 will result in
|
||||
* reduced precision due to the default 6 digits after the
|
||||
* decimal point.
|
||||
*
|
||||
* For numbers is < 0.1, we print with maximum precision and count
|
||||
* the number of zeros between the decimal point and the first
|
||||
* significant digit. We then print the number again with the
|
||||
* number of decimal places that gives us the required number of
|
||||
* significant digits. This ensures the number is correctly
|
||||
* rounded.
|
||||
*/
|
||||
if (fabs (d) >= 0.1) {
|
||||
snprintf (buffer, size, "%f", d);
|
||||
} else {
|
||||
snprintf (buffer, size, "%.18f", d);
|
||||
p = buffer;
|
||||
|
||||
if (*p == '+' || *p == '-')
|
||||
p++;
|
||||
if (*p == '+' || *p == '-')
|
||||
p++;
|
||||
|
||||
while (isdigit (*p))
|
||||
p++;
|
||||
while (isdigit (*p))
|
||||
p++;
|
||||
|
||||
if (strncmp (p, decimal_point, decimal_point_len) == 0)
|
||||
p += decimal_point_len;
|
||||
if (strncmp (p, decimal_point, decimal_point_len) == 0)
|
||||
p += decimal_point_len;
|
||||
|
||||
num_zeros = 0;
|
||||
while (*p++ == '0')
|
||||
num_zeros++;
|
||||
num_zeros = 0;
|
||||
while (*p++ == '0')
|
||||
num_zeros++;
|
||||
|
||||
decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
|
||||
decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
|
||||
|
||||
if (decimal_digits < 18)
|
||||
snprintf (buffer, size, "%.*f", decimal_digits, d);
|
||||
if (decimal_digits < 18)
|
||||
snprintf (buffer, size, "%.*f", decimal_digits, d);
|
||||
}
|
||||
}
|
||||
p = buffer;
|
||||
|
||||
|
|
@ -441,7 +463,10 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream,
|
|||
single_fmt, va_arg (ap, const char *));
|
||||
break;
|
||||
case 'f':
|
||||
_cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double));
|
||||
_cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE);
|
||||
break;
|
||||
case 'g':
|
||||
_cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE);
|
||||
break;
|
||||
case 'c':
|
||||
buffer[0] = va_arg (ap, int);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue