New module to perform decimal floating point arithmetic for charts.
[pspp] / tests / math / decimal-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2015 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20 #include <stdbool.h>
21 #include <assert.h>
22 #include <string.h>
23
24 #include "math/decimal.h"
25 #include <limits.h>
26 #include <float.h>
27 #include <math.h>
28
29 /* Canonicalise a string  holding the decimal representation of a number.
30    For example, leading zeros left of the decimal point are removed, as are
31    trailing zeros to the right.
32
33    This function is used purely for testing, and need not and is not intended
34    to be efficient.
35  */
36 char *
37 canonicalise_string (const char *s)
38 {
39   char *out;
40   char *dot = NULL;
41   bool negative = false;
42   char *p;
43   char *temp = malloc (strlen (s) + 3);
44   char *last_leading_zero = NULL;
45
46   /* Strip leading - if present */
47   if (*s == '-')
48     {
49       negative = true;
50       s++;
51     }
52   
53   strcpy (temp, "00");
54   strcat (temp, s);
55
56   char *first_trailing_zero = NULL;
57   char *significant_digit = NULL;
58   for (p = temp; *p; p++)
59     {
60       if (*p == '0' && dot == NULL && significant_digit == NULL)
61         last_leading_zero = p;
62
63       if (*p == '0' && first_trailing_zero == NULL)
64         first_trailing_zero = p;
65
66       if (*p == '.')
67         {
68           dot = p;
69           first_trailing_zero = NULL;
70         }
71
72       if (*p >= '1' && *p <= '9')
73         {
74           significant_digit = p;
75           first_trailing_zero = NULL;
76         }
77     }
78
79   if (first_trailing_zero && dot)
80     *first_trailing_zero = '\0';
81
82   if (last_leading_zero)
83     {
84       /* Strip leading zeros */
85       out = last_leading_zero + 1;
86
87       /* But if we now start with . put a zero back again */
88       if (dot == last_leading_zero + 1)
89         out--;
90     }
91
92
93   if (negative)
94     {
95       out--;
96       out[0] = '-';
97     }
98   
99   if (!significant_digit)
100     {
101       *out = '0';
102       *(out+1) = '\0';
103     }
104     
105
106   return out;
107 }
108
109
110 /* Tests both the decimal_to_string function, and the decimal_input_from_string 
111    function */
112 void
113 test_run (const char *input)
114   {
115     struct decimal test;
116     struct decimal number;
117     decimal_init_from_string (&number, input);
118
119     char *s = decimal_to_string (&number);
120     char *canon = canonicalise_string (input);
121     if (0 != strcmp (canon, s))
122       {
123         fprintf (stdout, "\"%s\" does not match \n\"%s\"\n", canon, s);
124         exit (1);
125       }
126
127     decimal_init_from_string (&test, s);
128     assert (0 == decimal_cmp (&test, &number));
129
130     free (s);
131   }
132
133
134 void
135 test_can (const char *in, const char *soll)
136 {
137   char *ist = canonicalise_string (in);
138   if (0 != strcmp (soll, ist))
139     {
140       printf ("\"%s\" canonicalises to \"%s\" (should be \"%s\")\n", in, ist, soll);
141     }
142 }
143
144
145 void
146 dump_scale (const struct decimal *low, const struct decimal *interval, int n_ticks)
147 {
148   int i;
149   struct decimal tick = *interval;
150   printf ("Lowest: %s\n", decimal_to_string (low));
151   for (i = 0; i <= n_ticks; ++i)
152     {
153       printf ("Tick %d: %s (%g)\n", i, decimal_to_string (&tick), decimal_to_double (&tick));
154       decimal_add (&tick, interval);
155     }
156 }
157
158
159
160 void
161 test_ceil (double x)
162 {
163   struct decimal dx;
164   decimal_from_double (&dx, x);
165   int act = decimal_ceil (&dx);
166   int expected = ceil (x);
167   
168   assert (act == expected);
169 }
170
171 void
172 test_floor (double x)
173 {
174   struct decimal dx;
175   decimal_from_double (&dx, x);
176   int act = decimal_floor (&dx);
177   int expected = floor (x);
178   
179   assert (act == expected);
180 }
181
182
183 void
184 test_addition (const struct decimal *one_, const struct decimal *two)
185 {
186   struct decimal one = *one_;
187   double d1 = decimal_to_double (&one);
188   double d2 = decimal_to_double (two);
189   
190   decimal_add (&one, two);
191   
192   double dsum = decimal_to_double (&one);
193
194   char sdsum1[256];
195   char sdsum2[256];
196
197   snprintf (sdsum1, 256, "%s", decimal_to_string (&one));
198   snprintf (sdsum2, 256, "%g", dsum);
199
200   assert (strcmp (sdsum1, sdsum2) == 0);
201 }
202
203
204 void
205 test_multiplication (const struct decimal *d, int m)
206 {
207   char b1[256];
208   char b2[256];
209   struct decimal dest = *d;
210   double x = decimal_to_double (&dest);
211
212   decimal_int_multiply (&dest, m);
213
214   double y = decimal_to_double (&dest);
215
216   snprintf (b1, 256, "%g", m * x);
217   snprintf (b2, 256, "%g", y);
218   assert (0 == strcmp (b1, b2));
219 }
220
221
222
223 int 
224 main (int argc, char **argv)
225 {
226   /* Test that our canonicalise function works for all corner cases we
227      can think of. */
228
229   test_can ("500", "500");
230   test_can ("5", "5");
231   test_can ("-3", "-3");
232   test_can ("-3.001", "-3.001");
233   test_can ("-03.001", "-3.001");
234   test_can ("-.0301", "-0.0301");
235   test_can ("0314.09", "314.09");
236   test_can ("0314.090", "314.09");
237   test_can ("0314.0900340", "314.090034");
238   test_can ("0.0", "0");
239   test_can ("0.", "0");
240   test_can (".0", "0");
241   test_can ("-.1", "-0.1");
242   test_can (".090", "0.09");
243   test_can ("03410.098700", "3410.0987");
244   test_can ("-03410.098700", "-3410.0987");
245
246   /* Test the conversion functions */
247   test_run ("-90000");
248   test_run ("-3");
249   test_run ("50001");
250   test_run ("500");
251   test_run ("350");
252   test_run ("050");
253   test_run ("4");
254   test_run ("0");
255   test_run (".45");
256   test_run ("-.45");
257   test_run ("666666666");
258   test_run ("6000000000");
259   test_run ("0.000000005");
260   test_run ("0.00000000000000000000000000000000000000005");
261   test_run ("0.0234");
262   test_run ("0.234");
263   test_run ("-0123.45600");
264
265   test_ceil (5.21);
266   test_ceil (-4.32);
267   test_ceil (0);
268   test_ceil (0.0009);
269
270   test_floor (4.09);
271   test_floor (-4.09);
272   test_floor (0);
273   test_floor (0.004);
274
275
276   {
277     struct decimal high = {2, 0};
278     struct decimal low = {2, -1};
279
280     test_addition (&high, &low);
281   }
282
283
284   {
285     struct decimal high = {10, 0};
286     struct decimal low = {2, -1};
287
288     test_addition (&high, &low);
289   }
290
291
292   {
293     struct decimal high = {10, 0};
294     struct decimal low = {-2, -1};
295
296     test_addition (&high, &low);
297   }
298
299   {
300     struct decimal high = {12, -5};
301     struct decimal low = {-2, -1};
302
303     test_addition (&high, &low);
304   }
305
306   {
307     struct decimal high = {-112, -1};
308     struct decimal low = {2, -1};
309
310     test_addition (&high, &low);
311   }
312
313
314   {
315     struct decimal m = {10, 0};
316
317     test_multiplication (&m, 11);
318   }
319
320
321   {
322     struct decimal m = {ORD_MAX - 2, 0};
323
324     test_multiplication (&m, 11);
325   }
326
327
328   {
329     struct decimal m = {34, 0};
330
331     test_multiplication (&m, 0);
332   }
333
334   {
335     struct decimal m = {34, -20};
336
337     test_multiplication (&m, 33);
338   }
339
340   {
341     struct decimal m = {304, 2};
342
343     test_multiplication (&m, -33);
344   }
345
346   return 0;
347 }