From 3c95184e5b984dbb02d67941f6253eb9783ed43c Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 6 Jan 2021 20:27:54 -0800 Subject: [PATCH] pivot-table: Implement SET SMALL. --- doc/utilities.texi | 9 ++++ src/data/settings.c | 14 ++++++ src/data/settings.h | 3 ++ src/language/utilities/set.q | 3 ++ src/output/pivot-table.c | 76 ++++++++++++++++++++++---------- src/output/pivot-table.h | 4 +- src/output/spv/spv-writer.c | 12 ++--- tests/language/stats/examine.at | 1 + tests/language/stats/factor.at | 1 + tests/language/stats/logistic.at | 2 +- tests/output/pivot-table.at | 72 ++++++++++++++++++++++++++++++ 11 files changed, 166 insertions(+), 31 deletions(-) diff --git a/doc/utilities.texi b/doc/utilities.texi index 0ddbcbb06c..1c75baa602 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -510,6 +510,7 @@ SET /CC@{A,B,C,D,E@}=@{'@var{npre},@var{pre},@var{suf},@var{nsuf}','@var{npre}.@var{pre}.@var{suf}.@var{nsuf}'@} /DECIMAL=@{DOT,COMMA@} /FORMAT=@var{fmt_spec} + /SMALL=@var{number} /WIB=@{NATIVE,MSBFIRST,LSBFIRST,VAX@} /WRB=@{NATIVE,ISL,ISB,IDL,IDB,VF,VD,VG,ZS,ZL@} @@ -752,6 +753,14 @@ The default @subcmd{DOT} setting causes the decimal point character to be Allows the default numeric input/output format to be specified. The default is F8.2. @xref{Input and Output Formats}. +@item SMALL +This controls how @pspp{} formats small numbers in pivot tables, in +cases where @pspp{} does not otherwise have a well-defined format for +the numbers. When such a number has a magnitude less than the value +set here, @pspp{} formats the number in scientific notation; +otherwise, it formats it in standard notation. The default is 0.0001. +Set a value of 0 to disable scientific notation. + @item WIB @anchor{SET WIB} diff --git a/src/data/settings.c b/src/data/settings.c index 6a336af6a0..1b48299446 100644 --- a/src/data/settings.c +++ b/src/data/settings.c @@ -71,6 +71,7 @@ struct settings int syntax; struct fmt_settings styles; + double small; enum settings_output_devices output_routing[SETTINGS_N_OUTPUT_TYPES]; @@ -111,6 +112,7 @@ static struct settings the_settings = { ENHANCED, /* global_algorithm */ ENHANCED, /* syntax */ FMT_SETTINGS_INIT, /* styles */ + .0001, /* small */ /* output_routing */ {SETTINGS_DEVICE_LISTING | SETTINGS_DEVICE_TERMINAL, @@ -551,6 +553,18 @@ settings_get_fmt_settings (void) return &the_settings.styles; } +double +settings_get_small (void) +{ + return the_settings.small; +} + +void +settings_set_small (double small) +{ + the_settings.small = small; +} + /* Returns a string of the form "$#,###.##" according to FMT, which must be of type FMT_DOLLAR. The caller must free the string. */ diff --git a/src/data/settings.h b/src/data/settings.h index f485ce152c..39bac54430 100644 --- a/src/data/settings.h +++ b/src/data/settings.h @@ -146,6 +146,9 @@ void settings_set_decimal_char (char decimal); const struct fmt_settings *settings_get_fmt_settings (void); +double settings_get_small (void); +void settings_set_small (double); + char * settings_dollar_template (const struct fmt_spec *fmt); /* Routing of different kinds of output. */ diff --git a/src/language/utilities/set.q b/src/language/utilities/set.q index 159c9bef6b..aa7f8b8034 100644 --- a/src/language/utilities/set.q +++ b/src/language/utilities/set.q @@ -110,6 +110,7 @@ int tgetnum (const char *); scompression=scompress:on/off; scripttab=string; seed=custom; + small=double; tnumbers=custom; tvars=custom; tb1=string; @@ -195,6 +196,8 @@ cmd_set (struct lexer *lexer, struct dataset *ds) settings_set_safer_mode (); if (cmd.sbc_scompression) settings_set_scompression (cmd.scompress == STC_ON); + if (cmd.sbc_small) + settings_set_small (cmd.n_small[0]); if (cmd.sbc_undefined) settings_set_undefined (cmd.undef == STC_WARN); if (cmd.sbc_wib) diff --git a/src/output/pivot-table.c b/src/output/pivot-table.c index 22cd20de13..b3d6c399da 100644 --- a/src/output/pivot-table.c +++ b/src/output/pivot-table.c @@ -18,6 +18,7 @@ #include "output/pivot-table.h" +#include #include #include "data/data-out.h" @@ -45,8 +46,8 @@ #define _(msgid) gettext (msgid) #define N_(msgid) msgid -static const struct fmt_spec *pivot_table_get_format ( - const struct pivot_table *, const char *s); +static void pivot_table_use_rc (const struct pivot_table *, const char *s, + struct fmt_spec *, bool *honor_small); /* Pivot table display styling. */ @@ -394,17 +395,19 @@ pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis) static void pivot_category_set_rc (struct pivot_category *category, const char *s) { - const struct fmt_spec *format = pivot_table_get_format ( - category->dimension->table, s); - if (format) - category->format = *format; + if (!s) + return; + + pivot_table_use_rc (category->dimension->table, s, + &category->format, &category->honor_small); /* Ensure that the category itself, in addition to the cells within it, takes the format. (It's kind of rare for a category to have a numeric format though.) */ struct pivot_value *name = category->name; if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w) - name->numeric.format = format ? *format : *settings_get_format (); + pivot_table_use_rc (category->dimension->table, s, + &name->numeric.format, &name->numeric.honor_small); } static void @@ -764,19 +767,35 @@ pivot_result_class_find (const char *s) return NULL; } -static const struct fmt_spec * -pivot_table_get_format (const struct pivot_table *table, const char *s) +static void +pivot_table_use_rc (const struct pivot_table *table, const char *s, + struct fmt_spec *format, bool *honor_small) { - if (!s) - return NULL; - else if (!strcmp (s, PIVOT_RC_OTHER)) - return settings_get_format (); - else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format) - return &table->weight_format; - else + if (s) { - const struct result_class *rc = pivot_result_class_find (s); - return rc ? &rc->format : NULL; + if (!strcmp (s, PIVOT_RC_OTHER)) + { + *format = *settings_get_format (); + *honor_small = true; + } + else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format) + { + *format = table->weight_format; + *honor_small = false; + } + else + { + const struct result_class *rc = pivot_result_class_find (s); + if (rc) + { + *format = rc->format; + *honor_small = false; + } + else + { + printf ("unknown class %s\n", s); + } + } } } @@ -854,6 +873,7 @@ pivot_table_create__ (struct pivot_value *title, const char *subtype) table->command_c = output_get_command_name (); table->look = pivot_table_look_ref (pivot_table_look_get_default ()); table->settings = fmt_settings_copy (settings_get_fmt_settings ()); + table->small = settings_get_small (); hmap_init (&table->cells); @@ -1414,11 +1434,13 @@ pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n, if (c->format.w) { value->numeric.format = c->format; + value->numeric.honor_small = c->honor_small; goto done; } } } value->numeric.format = *settings_get_format (); + value->numeric.honor_small = true; done:; } @@ -2288,8 +2310,17 @@ pivot_value_format_body (const struct pivot_value *value, value->numeric.value_label != NULL); if (show & SETTINGS_VALUE_SHOW_VALUE) { + const struct fmt_spec *f = &value->numeric.format; + const struct fmt_spec *format + = (f->type == FMT_F + && value->numeric.honor_small + && value->numeric.x != 0 + && fabs (value->numeric.x) < pt->small + ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d } + : f); + char *s = data_out (&(union value) { .f = value->numeric.x }, - "UTF-8", &value->numeric.format, &pt->settings); + "UTF-8", format, &pt->settings); ds_put_cstr (out, s + strspn (s, " ")); free (s); } @@ -2817,10 +2848,7 @@ pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value, const char *rc) { if (value->type == PIVOT_VALUE_NUMERIC) - { - const struct fmt_spec *f = pivot_table_get_format (table, rc); - if (f) - value->numeric.format = *f; - } + pivot_table_use_rc (table, rc, + &value->numeric.format, &value->numeric.honor_small); } diff --git a/src/output/pivot-table.h b/src/output/pivot-table.h index 343d7a3c09..e7537e781b 100644 --- a/src/output/pivot-table.h +++ b/src/output/pivot-table.h @@ -307,10 +307,11 @@ struct pivot_category bool show_label_in_corner; /* Leaf only. */ - struct fmt_spec format; size_t group_index; /* In ->parent->subs[]. */ size_t data_index; /* In ->dimension->data_leaves[]. */ size_t presentation_index; /* In ->dimension->presentation_leaves[]. */ + struct fmt_spec format; /* Default format for values in this category. */ + bool honor_small; /* Honor pivot_table 'small' setting? */ }; static inline bool @@ -660,6 +661,7 @@ struct pivot_value char *var_name; /* May be NULL. */ char *value_label; /* May be NULL. */ enum settings_value_show show; /* Show value or label or both? */ + bool honor_small; /* Honor value of pivot table 'small'? */ } numeric; diff --git a/src/output/spv/spv-writer.c b/src/output/spv/spv-writer.c index b8a4768fc0..19ea047afa 100644 --- a/src/output/spv/spv-writer.c +++ b/src/output/spv/spv-writer.c @@ -555,9 +555,10 @@ put_value_mod (struct buf *buf, const struct pivot_value *value, } static void -put_format (struct buf *buf, const struct fmt_spec *f) +put_format (struct buf *buf, const struct fmt_spec *f, bool honor_small) { - put_u32 (buf, (fmt_to_io (f->type) << 16) | (f->w << 8) | f->d); + int type = f->type == FMT_F && honor_small ? 40 : fmt_to_io (f->type); + put_u32 (buf, (type << 16) | (f->w << 8) | f->d); } static int @@ -585,7 +586,7 @@ put_value (struct buf *buf, const struct pivot_value *value) { put_byte (buf, 2); put_value_mod (buf, value, NULL); - put_format (buf, &value->numeric.format); + put_format (buf, &value->numeric.format, value->numeric.honor_small); put_double (buf, value->numeric.x); put_string (buf, value->numeric.var_name); put_string (buf, value->numeric.value_label); @@ -595,7 +596,7 @@ put_value (struct buf *buf, const struct pivot_value *value) { put_byte (buf, 1); put_value_mod (buf, value, NULL); - put_format (buf, &value->numeric.format); + put_format (buf, &value->numeric.format, value->numeric.honor_small); put_double (buf, value->numeric.x); } break; @@ -604,7 +605,8 @@ put_value (struct buf *buf, const struct pivot_value *value) put_byte (buf, 4); put_value_mod (buf, value, NULL); put_format (buf, - &(struct fmt_spec) { FMT_A, strlen (value->string.s), 0 }); + &(struct fmt_spec) { FMT_A, strlen (value->string.s), 0 }, + false); put_string (buf, value->string.value_label); put_string (buf, value->string.var_name); put_show_values (buf, value->string.show); diff --git a/tests/language/stats/examine.at b/tests/language/stats/examine.at index 6c76b62d6d..4e7d6f4d8f 100644 --- a/tests/language/stats/examine.at +++ b/tests/language/stats/examine.at @@ -726,6 +726,7 @@ begin data. 300 threehundred end data. +set small=0. examine x /statistics = extreme /id = y diff --git a/tests/language/stats/factor.at b/tests/language/stats/factor.at index 334b4ba72c..c066dae52b 100644 --- a/tests/language/stats/factor.at +++ b/tests/language/stats/factor.at @@ -1831,6 +1831,7 @@ BEGIN DATA 9 7 9 9 7 END DATA +SET SMALL=0. FACTOR /VARIABLES=TRAIT1 TO TRAIT5 /ROTATION=NOROTATE /* NOROTATE may have caused the problem. */ /EXTRACTION=PC diff --git a/tests/language/stats/logistic.at b/tests/language/stats/logistic.at index cf413ea6e0..93882d336c 100644 --- a/tests/language/stats/logistic.at +++ b/tests/language/stats/logistic.at @@ -1011,7 +1011,7 @@ AT_DATA([lr-cat2.data], [dnl ]) AT_DATA([stringcat.sps], [dnl -set format=F20.3. +set format=F20.3 /small=0. data list notable file='lr-cat2.data' list /read honcomp wiz science *. string ses(a1). diff --git a/tests/output/pivot-table.at b/tests/output/pivot-table.at index bc66c828f4..19b1930ccc 100644 --- a/tests/output/pivot-table.at +++ b/tests/output/pivot-table.at @@ -539,3 +539,75 @@ d3 │c1 54│55┊56│57│58┊59│60│61┊62 │ ┊c3 72│73┊74│75│76┊77│78│79┊80 ]) AT_CLEANUP + +AT_SETUP([pivot table - small numbers]) +AT_DATA([pivot.txt], [[ +/title "small numbers" +/row "exponent"*("0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9") +/col "sign"*("positive", "negative") +/col "result class"*("general" RC_OTHER, "specific" RC_RESIDUAL) +/cell[0, 0, 0] = 1 +/cell[1, 0, 0] = .1 +/cell[2, 0, 0] = .01 +/cell[3, 0, 0] = .001 +/cell[4, 0, 0] = .0001 +/cell[5, 0, 0] = .00001 +/cell[6, 0, 0] = .000001 +/cell[7, 0, 0] = .0000001 +/cell[8, 0, 0] = .00000001 +/cell[9, 0, 0] = .000000001 +/cell[0, 0, 1] = -1 +/cell[1, 0, 1] = -.1 +/cell[2, 0, 1] = -.01 +/cell[3, 0, 1] = -.001 +/cell[4, 0, 1] = -.0001 +/cell[5, 0, 1] = -.00001 +/cell[6, 0, 1] = -.000001 +/cell[7, 0, 1] = -.0000001 +/cell[8, 0, 1] = -.00000001 +/cell[9, 0, 1] = -.000000001 +/cell[0, 1, 0] = 1 +/cell[1, 1, 0] = .1 +/cell[2, 1, 0] = .01 +/cell[3, 1, 0] = .001 +/cell[4, 1, 0] = .0001 +/cell[5, 1, 0] = .00001 +/cell[6, 1, 0] = .000001 +/cell[7, 1, 0] = .0000001 +/cell[8, 1, 0] = .00000001 +/cell[9, 1, 0] = .000000001 +/cell[0, 1, 1] = -1 +/cell[1, 1, 1] = -.1 +/cell[2, 1, 1] = -.01 +/cell[3, 1, 1] = -.001 +/cell[4, 1, 1] = -.0001 +/cell[5, 1, 1] = -.00001 +/cell[6, 1, 1] = -.000001 +/cell[7, 1, 1] = -.0000001 +/cell[8, 1, 1] = -.00000001 +/cell[9, 1, 1] = -.000000001 +]]) +AT_CHECK([pivot-table-test --table-look $srcdir/output/look.stt pivot.txt --box unicode], [0], [dnl +small numbers +╭────────┬─────────────────────────────────────╮ +│ │ result class │ +│ ├───────────────────┬─────────────────┤ +│ │ general │ specific │ +│ ├───────────────────┼─────────────────┤ +│ │ sign │ sign │ +│ ├─────────┬─────────┼────────┬────────┤ +│exponent│ positive│ negative│positive│negative│ +├────────┼─────────┼─────────┼────────┼────────┤ +│0 │ 1.00│ 1.00│ -1.00│ -1.00│ +│-1 │ .10│ .10│ -.10│ -.10│ +│-2 │ .01│ .01│ -.01│ -.01│ +│-3 │ .00│ .00│ .00│ .00│ +│-4 │ .00│ .00│ .00│ .00│ +│-5 │1.00E-005│1.00E-005│ .00│ .00│ +│-6 │1.00E-006│1.00E-006│ .00│ .00│ +│-7 │1.00E-007│1.00E-007│ .00│ .00│ +│-8 │1.00E-008│1.00E-008│ .00│ .00│ +│-9 │1.00E-009│1.00E-009│ .00│ .00│ +╰────────┴─────────┴─────────┴────────┴────────╯ +]) +AT_CLEANUP \ No newline at end of file -- 2.30.2