From 9743aa05898c191b3c2d3fead5ae0315beb44bb3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 7 Mar 2015 00:36:42 -0800 Subject: [PATCH] expressions: Add support for 2- and 3-operand RND function. Requested by Tom Smekens. --- NEWS | 5 +++++ doc/expressions.texi | 10 +++++++--- doc/utilities.texi | 9 +++++++++ src/data/settings.c | 16 +++++++++++++++- src/data/settings.h | 5 ++++- src/language/expressions/helpers.c | 16 +++++++++++++++- src/language/expressions/helpers.h | 2 ++ src/language/expressions/operations.def | 6 ++++-- src/language/utilities/set.q | 18 +++++++++++++++++- tests/language/expressions/evaluate.at | 13 ++++++++++++- 10 files changed, 90 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index c1746d6bc8..ed05306562 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,11 @@ Changes since 0.8.4: * The GRAPH command is now available. Initially it supports scatterplots and histograms. + * The RND operator in expressions now supports additional operands + for rounding to values other than integers and to indicate a level + of rounding fuzz. The default rounding fuzz may now be controlled + and displayed with SET FUZZBITS and SHOW FUZZBITS, respectively. + Changes from 0.8.3 to 0.8.4: * Formatting of SYSFILE INFO output was made easier to read. diff --git a/doc/expressions.texi b/doc/expressions.texi index 1570d0dfb4..7180bf8793 100644 --- a/doc/expressions.texi +++ b/doc/expressions.texi @@ -331,9 +331,13 @@ Returns the remainder when @var{number} is divided by 10. If @end deftypefn @cindex rounding -@deftypefn {Function} {} RND (@var{number}) -Takes the absolute value of @var{number} and rounds it to an integer. -Then, if @var{number} was negative originally, negates the result. +@deftypefn {Function} {} RND (@var{number} [, @var{mult}[, @var{fuzzbits}]]) +Rounds @var{number} and rounds it to a multiple of @var{mult} (by +default 1). Halves are rounded away from zero, as are values that +fall short of halves by less than @var{fuzzbits} of errors in the +least-significant bits of @var{number}. If @var{fuzzbits} is not +specified then the default is taken from SET FUZZBITS (@pxref{SET +FUZZBITS}), which is 6 unless overridden. @end deftypefn @cindex truncation diff --git a/doc/utilities.texi b/doc/utilities.texi index da5b7b0daa..eb30f892d5 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -440,6 +440,7 @@ SET /MXLOOPS=@var{max_loops} /SEED=@{RANDOM,@var{seed_value}@} /UNDEFINED=@{WARN,NOWARN@} + /FUZZBITS=@var{fuzzbits} (data output) /CC@{A,B,C,D,E@}=@{'@var{npre},@var{pre},@var{suf},@var{nsuf}','@var{npre}.@var{pre}.@var{suf}.@var{nsuf}'@} @@ -642,6 +643,13 @@ RANDOM, which will obtain an initial seed from the current time of day. @item UNDEFINED Currently not used. +@item FUZZBITS +@anchor{SET FUZZBITS} +The maximum number of bits of errors in the least-significant places +to accept for rounding up a value that is almost halfway between two +possibilities for rounding with the RND operator (@pxref{Miscellaneous +Mathematics}). The default @var{fuzzbits} is 6. + @item WORKSPACE The maximum amount of memory (in kilobytes) that @pspp{} will use to store data being processed. If memory in excess of the workspace size is required, then @pspp{} will start @@ -878,6 +886,7 @@ SHOW [DIRECTORY] [ENVIRONMENT] [FORMAT] + [FUZZBITS] [LENGTH] [MXERRS] [MXLOOPS] diff --git a/src/data/settings.c b/src/data/settings.c index 4c0fde0837..5c4e6899b9 100644 --- a/src/data/settings.c +++ b/src/data/settings.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -66,6 +66,7 @@ struct settings size_t workspace; struct fmt_spec default_format; bool testing_mode; + int fuzzbits; int cmd_algorithm; int global_algorithm; @@ -108,6 +109,7 @@ static struct settings the_settings = { 64L * 1024 * 1024, /* workspace */ {FMT_F, 8, 2}, /* default_format */ false, /* testing_mode */ + 6, /* fuzzbits */ ENHANCED, /* cmd_algorithm */ ENHANCED, /* global_algorithm */ ENHANCED, /* syntax */ @@ -487,6 +489,18 @@ settings_set_testing_mode ( bool testing_mode) the_settings.testing_mode = testing_mode; } +int +settings_get_fuzzbits (void) +{ + return the_settings.fuzzbits; +} + +void +settings_set_fuzzbits (int fuzzbits) +{ + the_settings.fuzzbits = fuzzbits; +} + /* Return the current algorithm setting */ enum behavior_mode settings_get_algorithm (void) diff --git a/src/data/settings.h b/src/data/settings.h index 409b7fe9eb..c499293ebb 100644 --- a/src/data/settings.h +++ b/src/data/settings.h @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -102,6 +102,9 @@ void settings_set_format ( const struct fmt_spec *); bool settings_get_testing_mode (void); void settings_set_testing_mode (bool); +int settings_get_fuzzbits (void); +void settings_set_fuzzbits (int); + enum settings_var_style { SETTINGS_VAR_STYLE_NAMES, diff --git a/src/language/expressions/helpers.c b/src/language/expressions/helpers.c index 720d910f68..5aad13eb16 100644 --- a/src/language/expressions/helpers.c +++ b/src/language/expressions/helpers.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2008, 2010, 2011 Free Software Foundation, Inc. + Copyright (C) 2008, 2010, 2011, 2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -661,3 +661,17 @@ npdf_beta (double x, double a, double b, double lambda) return sum; } } + +double +round_nearest (double x, double mult, double fuzzbits) +{ + double adjustment; + + if (fuzzbits <= 0) + fuzzbits = settings_get_fuzzbits (); + adjustment = .5 + exp2 (fuzzbits - DBL_MANT_DIG); + + x /= mult; + x = x >= 0. ? floor (x + adjustment) : -floor (-x + adjustment); + return x * mult; +} diff --git a/src/language/expressions/helpers.h b/src/language/expressions/helpers.h index ef4fdadd4d..0d349f7fd9 100644 --- a/src/language/expressions/helpers.h +++ b/src/language/expressions/helpers.h @@ -81,4 +81,6 @@ double cdf_bvnor (double x0, double x1, double r); double idf_fdist (double P, double a, double b); +double round_nearest (double x, double mult, double fuzzbits); + #endif /* expressions/helpers.h */ diff --git a/src/language/expressions/operations.def b/src/language/expressions/operations.def index 37af54a911..60ed2eac20 100644 --- a/src/language/expressions/operations.def +++ b/src/language/expressions/operations.def @@ -1,7 +1,7 @@ // -*- c -*- // // PSPP - a program for statistical analysis. -// Copyright (C) 2005, 2006, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +// Copyright (C) 2005, 2006, 2009, 2010, 2011, 2012, 2015 Free Software Foundation, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -84,7 +84,9 @@ function LG10(x) = check_errno (log10 (x)); function LN (x) = check_errno (log (x)); function LNGAMMA (x >= 0) = gsl_sf_lngamma (x); function MOD10 (x) = fmod (x, 10); -function RND (x) = x >= 0. ? floor (x + .5) : -floor (-x + .5); +function RND (x) = round_nearest (x, 1, 0); +function RND (x, mult != 0) = round_nearest (x, mult, 0); +function RND (x, mult != 0, fuzzbits >= 0) = round_nearest (x, mult, fuzzbits); function SIN (x) = sin (x); function SQRT (x >= 0) = sqrt (x); function TAN (x) = check_errno (tan (x)); diff --git a/src/language/utilities/set.q b/src/language/utilities/set.q index f90e3e14de..0be8c47010 100644 --- a/src/language/utilities/set.q +++ b/src/language/utilities/set.q @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -80,6 +80,7 @@ int tgetnum (const char *); epoch=custom; errors=custom; format=custom; + fuzzbits=integer; headers=headers:no/yes/blank; highres=hires:on/off; histogram=string; @@ -153,6 +154,14 @@ cmd_set (struct lexer *lexer, struct dataset *ds) if (cmd.sbc_decimal) settings_set_decimal_char (cmd.dec == STC_DOT ? '.' : ','); + if (cmd.sbc_fuzzbits) + { + int fuzzbits = cmd.n_fuzzbits[0]; + if (fuzzbits >= 0 && fuzzbits <= 20) + settings_set_fuzzbits (fuzzbits); + else + msg (SE, _("%s must be between 0 and 20."), "FUZZBITS"); + } if (cmd.sbc_include) settings_set_include (cmd.inc == STC_ON); @@ -710,6 +719,12 @@ show_format (const struct dataset *ds UNUSED) return xstrdup (fmt_to_string (settings_get_format (), str)); } +static char * +show_fuzzbits (const struct dataset *ds UNUSED) +{ + return xasprintf ("%d", settings_get_fuzzbits ()); +} + static char * show_journal (const struct dataset *ds UNUSED) { @@ -952,6 +967,7 @@ const struct show_sbc show_table[] = {"ENVIRONMENT", show_system}, {"ERRORS", show_errors}, {"FORMAT", show_format}, + {"FUZZBITS", show_fuzzbits}, {"JOURNAL", show_journal}, {"LENGTH", show_length}, {"LOCALE", show_locale}, diff --git a/tests/language/expressions/evaluate.at b/tests/language/expressions/evaluate.at index cd514fef93..846eee482b 100644 --- a/tests/language/expressions/evaluate.at +++ b/tests/language/expressions/evaluate.at @@ -334,8 +334,19 @@ CHECK_EXPR_EVAL([exp lg10 ln sqrt abs mod mod10 rnd trunc], [[rnd(5.6)], [6.00]], [[rnd(-5.4)], [-5.00]], [[rnd(-5.6)], [-6.00]], + [[rnd(5.56, .1)], [5.60]], + [[rnd(-5.56, .1)], [-5.60]], + [[rnd(.5)], [1.00]], + [[rnd(.5 - 2**-53)], [1.00]], + [[rnd(.5 - 2**-52)], [1.00]], + [[rnd(.5 - 2**-51)], [1.00]], + [[rnd(.5 - 2**-45)], [0.00]], + [[rnd(.5 - 2**-45, 1, 10)], [1.00]], [[rnd('x')], [error], - [error: DEBUG EVALUATE: Type mismatch invoking RND(number) as rnd(string).]], + [error: DEBUG EVALUATE: Function invocation rnd(string) does not match any known function. Candidates are: +RND(number) +RND(number, number) +RND(number, number, number).]], [[trunc(1.2)], [1.00]], [[trunc(1.9)], [1.00]], -- 2.30.2