X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fmath%2Fhistogram.c;h=b00067210f08676736d589660c9eb85f75216831;hb=563b6160d2ed20120fbb62410a65e03c28537383;hp=ca2d9d7d20f1d93517bbaa71e2f36f7f9aa54791;hpb=8e6c2074463a02f285c6b0be4b67f82f16b52c50;p=pspp diff --git a/src/math/histogram.c b/src/math/histogram.c index ca2d9d7d20..b00067210f 100644 --- a/src/math/histogram.c +++ b/src/math/histogram.c @@ -21,6 +21,7 @@ #include #include +#include "data/settings.h" #include "libpspp/message.h" #include "libpspp/assertion.h" #include "libpspp/cast.h" @@ -84,6 +85,10 @@ double get_slack (double limit, double half_bin_width, int *n_half_bins) ADJ_MIN and ADJ_MAX are locations of the adjusted values of MIN and MAX (the range will always be equal or slightly larger). Returns the number of bins. + + The "testing_assert" expressions in this function should be algebraically correct. + However, due to floating point rounding they could fail, especially when small numbers + are involved. In normal use, therefore, testing_assert does nothing. */ static int adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, double *adj_max) @@ -97,7 +102,7 @@ adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, do double lower_slack = get_slack (min, half_bin_width, &lower_limit); double upper_slack = -get_slack (max, half_bin_width, &upper_limit); - assert (max > min); + testing_assert (max > min); /* If min is negative, then lower_slack may be less than zero. In this case, the lower bound must be extended in the negative direction @@ -108,7 +113,7 @@ adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, do lower_limit--; lower_slack += half_bin_width; } - assert (lower_limit * half_bin_width <= min); + testing_assert (lower_limit * half_bin_width <= min); /* However, the upper bound must be extended regardless, because histogram bins span the range [lower, upper). In other words, the upper bound must be @@ -116,7 +121,7 @@ adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, do */ upper_limit++;; upper_slack += half_bin_width; - assert (upper_limit * half_bin_width > max); + testing_assert (upper_limit * half_bin_width > max); /* The range must be an EVEN number of half bin_widths */ if ( (upper_limit - lower_limit) % 2) @@ -140,29 +145,43 @@ adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, do */ if ( lower_limit % 2 == 0) { - if (upper_slack > lower_slack && upper_slack > half_bin_width) + /* If there is not enough slack at either end to perform a shift, + then we must extend the range so that there is. We must extend + by two half bin widths in order to preserve the EVEN condition + established above. Also, we extend on the end with the least + slack, in order to keep things as balanced as possible. */ + if ( upper_slack > lower_slack && upper_slack <= half_bin_width) + { + lower_limit -= 2; + lower_slack += 2 * half_bin_width; + } + + if (lower_slack > upper_slack && lower_slack < half_bin_width) + { + upper_limit += 2; + upper_slack += 2 * half_bin_width; + } + + if (upper_slack > lower_slack) { + testing_assert (upper_slack > half_bin_width); + /* Adjust the range to the left */ lower_limit --; upper_limit --; upper_slack -= half_bin_width; lower_slack += half_bin_width; } - else if (lower_slack > upper_slack && lower_slack >= half_bin_width) + else { + testing_assert (lower_slack >= half_bin_width); + /* Adjust the range to the right */ lower_limit ++; upper_limit ++; lower_slack -= half_bin_width; upper_slack += half_bin_width; } - else - { - /* In this case, we cannot adjust in either direction. - To get the most pleasing alignment, we would have to change - the bin width (which would have other visual disadvantages). - */ - } } /* If there are any completely empty bins, then remove them, @@ -183,8 +202,8 @@ adjust_bin_ranges (double bin_width, double min, double max, double *adj_min, do *adj_min = lower_limit * half_bin_width; *adj_max = upper_limit * half_bin_width; - assert (*adj_max > max); - assert (*adj_min <= min); + testing_assert (*adj_max > max); + testing_assert (*adj_min <= min); return (upper_limit - lower_limit) / 2.0; } @@ -208,12 +227,14 @@ histogram_create (double bin_width, double min, double max) assert (bin_width > 0); + bin_width = chart_rounded_tick (bin_width); bins = adjust_bin_ranges (bin_width, min, max, &adjusted_min, &adjusted_max); /* Force the number of bins to lie in a sensible range. */ if (bins > MAX_BINS) { - bins = adjust_bin_ranges ((max - min) / (double) (MAX_BINS - 1), + bin_width = chart_rounded_tick ((max - min) / (double) (MAX_BINS - 1)); + bins = adjust_bin_ranges (bin_width, min, max, &adjusted_min, &adjusted_max); }