Advertisement
homer512

Shewchuk

Jul 26th, 2014
277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.42 KB | None | 0 0
  1. /*
  2.  * Copyright 2013 Florian Philipp
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  * http://www.apache.org/licenses/LICENSE-2.0
  9.  
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. #include <math.h>
  18. /* using fabs */
  19. #include <stdlib.h>
  20. /* using malloc, realloc, free, NULL */
  21. #include <stdio.h>
  22. /* using printf, fopen, fwrite, fclose */
  23.  
  24.  
  25. /**
  26.  * Shewchuk summation algorithm.
  27.  *
  28.  * Derived from msum here:
  29.  * http://code.activestate.com/recipes/393090/
  30.  */
  31. struct Accumulator
  32. {
  33.   /**
  34.    * Number of currently used partial sums
  35.    */
  36.   size_t partials_n;
  37.   /**
  38.    * Capacity for partial sums
  39.  
  40.    * partials_cap == 1 indicates use of partials.scalar
  41.    */
  42.   size_t partials_cap;
  43.   /**
  44.    * Partial sums ordered by value
  45.    *
  46.    * To avoid dynamic allocation in the common case that only one sum is kept,
  47.    * the scalar union member is used.
  48.    * The additional if/else comparisons have negligible effect on the run time
  49.    */
  50.   union { double* array; double scalar; } partials;
  51. };
  52.  
  53.  
  54. /**
  55.  * Initializes an Accumulator
  56.  *
  57.  * An Accumulator may only be initialized once to avoid memory leaks
  58.  * unless it is destroyed between initializations.
  59.  * See also: accumulator_reset
  60.  */
  61. void accumulator_init(struct Accumulator* self)
  62. {
  63.   self->partials_n = 0;
  64.   self->partials_cap = 1;
  65.   self->partials.scalar = 0.;
  66. }
  67.  
  68. /**
  69.  * Releases all resources associated with this Accumulator
  70.  *
  71.  * May only be called once to avoid segfaults.
  72.  * A destroyed Accumulator may only be used after calling accumulator_init.
  73.  * As a rule of thumb, every accumulator_init should be paired with an
  74.  * accumulator_destroy
  75.  */
  76. void accumulator_destroy(struct Accumulator* self)
  77. {
  78.   if(self->partials_cap > 1)
  79.     free(self->partials.array);
  80. }
  81.  
  82. /**
  83.  * Re-initializes an Accumulator in order to use it for another summation
  84.  */
  85. void accumulator_reset(struct Accumulator* self)
  86. {
  87.   self->partials_n = 0;
  88.   if(self->partials_cap == 1)
  89.     self->partials.scalar = 0.;
  90. }
  91.  
  92. /**
  93.  * Private method of Accumulator. Updates existing partial sums
  94.  *
  95.  * \param self an initialized Accumulator
  96.  * \param x a value to be added
  97.  * \param tail output argument. Returns the index of the last used partial sum.
  98.  * Is less or equal to self->partials_n
  99.  *
  100.  * \return the last partial sum. Has to be stored in self->partials[tail]
  101.  */
  102. static double _accumulator_update_partials(struct Accumulator* self,
  103.                        double x,
  104.                        size_t* tail)
  105. {
  106.   *tail = 0;
  107.   double* partials;
  108.   if(self->partials_cap > 1)
  109.     partials = self->partials.array;
  110.   else
  111.     partials = &self->partials.scalar;
  112.   size_t partial_i;
  113.   for(partial_i = 0; partial_i < self->partials_n; ++partial_i) {
  114.     double y = partials[partial_i];
  115.     if(fabs(x) < fabs(y)) {
  116.       double tmp = y;
  117.       y = x;
  118.       x = tmp;
  119.     }
  120.     double hi = x + y;
  121.     double lo = y - (hi - x);
  122.     if(lo != 0.) {
  123.       partials[*tail] = lo;
  124.       *tail += 1;
  125.     }
  126.     x = hi;
  127.   }
  128.   return x;
  129. }
  130.  
  131. /**
  132.  * Private method of Accumulator. Extends array of partial sums if required
  133.  *
  134.  * Updates self->partials_cap and self->partials.
  135.  * May move data from self->partials.scalar to self->partials.array
  136.  *
  137.  * \param self an initialized Accumulator
  138.  * \param tail the last index that has to be used
  139.  *
  140.  * \return 0 on success. 1 if memory allocation failed
  141.  */
  142. int _accumulator_reserve(struct Accumulator* self, size_t tail)
  143. {
  144.   if(tail < self->partials_cap)
  145.     return 0;
  146.   size_t reserved = (tail + 1) * 2;
  147.   double* partials_ext;
  148.   if(self->partials_cap == 1) {
  149.     partials_ext = malloc(reserved * sizeof(double));
  150.     if(! partials_ext)
  151.       return 1;
  152.     partials_ext[0] = self->partials.scalar;
  153.   }
  154.   else {
  155.     partials_ext = realloc(self->partials.array, reserved * sizeof(double));
  156.     if(! partials_ext)
  157.       return 1;
  158.   }
  159.   self->partials.array = partials_ext;
  160.   self->partials_cap = reserved;
  161.   return 0;
  162. }
  163.  
  164. /**
  165.  * Adds a value to the accumulated sum
  166.  *
  167.  * \return 0 on success. 1 if resource allocation failed
  168.  */
  169. int accumulator_add(struct Accumulator* self, double x)
  170. {
  171.   size_t tail;
  172.   x = _accumulator_update_partials(self, x, &tail);
  173.   if(_accumulator_reserve(self, tail))
  174.     return 1;
  175.   self->partials_n = tail + 1;
  176.   if(self->partials_cap > 1)
  177.     self->partials.array[tail] = x;
  178.   else
  179.     self->partials.scalar = x;
  180.   return 0;
  181. }
  182.  
  183. /**
  184.  * Returns the sum of all added values
  185.  *
  186.  * Note that this call has some overhead. Results should be cached
  187.  */
  188. double accumulator_sum(const struct Accumulator* self)
  189. {
  190.   if(self->partials_cap == 1)
  191.     return self->partials.scalar;
  192.   double sum = 0.;
  193.   size_t i;
  194.   for(i = 0; i < self->partials_n; ++i)
  195.     sum += self->partials.array[i];
  196.   return sum;
  197. }
  198.  
  199.  
  200. /**
  201.  * Kahan summation algorithm
  202.  */
  203. struct Kahan
  204. {
  205.   double sum, compensation;
  206. };
  207.  
  208. void kahan_init(struct Kahan* self)
  209. {
  210.   self->sum = self->compensation = 0.;
  211. }
  212.  
  213. void kahan_add(struct Kahan* self, double added)
  214. {
  215.   double y = added - self->compensation;
  216.   double t = self->sum + y;
  217.   self->compensation = (t - self->sum) - y;
  218.   self->sum = t;
  219. }
  220.  
  221. double kahan_sum(const struct Kahan* self)
  222. {
  223.   return self->sum;
  224. }
  225.  
  226. void test_shewchuk(const double* items, size_t n, size_t repetitions)
  227. {
  228.   struct Accumulator accum;
  229.   accumulator_init(&accum);
  230.   size_t rep_i;
  231.   for(rep_i = 0; rep_i < repetitions; ++rep_i) {
  232.     size_t i;
  233.     for(i = 0; i < n; ++i) {
  234.       if(accumulator_add(&accum, items[i])) {
  235.     printf("OOM\n");
  236.     goto dtor;
  237.       }
  238.     }
  239.   }
  240.   printf("Shewchuk: %g\n", accumulator_sum(&accum));
  241.  dtor:
  242.   accumulator_destroy(&accum);
  243. }
  244.  
  245. void test_kahan(const double* items, size_t n, size_t repetitions)
  246. {
  247.   struct Kahan kahan;
  248.   kahan_init(&kahan);
  249.   size_t rep_i;
  250.   for(rep_i = 0; rep_i < repetitions; ++rep_i) {
  251.     size_t i;
  252.     for(i = 0; i < n; ++i)
  253.       kahan_add(&kahan, items[i]);
  254.   }
  255.   printf("Kahan: %g\n", kahan_sum(&kahan));
  256. }
  257.  
  258. void test_devnull(const double* items, size_t n)
  259. {
  260.   FILE* fd = fopen("/dev/null", "w");
  261.   if(! fd)
  262.     return;
  263.   fwrite(items, sizeof(*items), n, fd);
  264.   fclose(fd);
  265. }
  266.  
  267. static void print_test(const double* items, size_t n_items, size_t repetitions)
  268. {
  269.   printf("Testing summation of {");
  270.   if(n_items) {
  271.     printf("%g", items[0]);
  272.     size_t i;
  273.     for(i = 1; i < n_items; ++i)
  274.       printf(", %g", items[i]);
  275.   }
  276.   printf("} * %zd\n", repetitions);
  277. }
  278.  
  279. #if ! (defined(WITH_SHEW) || defined(WITH_KAHAN) || defined(WITH_DEVNULL))
  280. #  error No algorithm defined.
  281. #  error Use -DWITH_SHEW and/or -DWITH_KAHAN, or -DWITH_DEVNULL
  282. #endif
  283.  
  284. int main()
  285. {
  286.   static const double items[] = {1., 1e16, 1., -1e16};
  287.   const size_t n_items = sizeof(items) / sizeof(items[0]);
  288. #ifdef REPETITIONS
  289.   const size_t repetitions = REPETITIONS;
  290. #else
  291.   const size_t repetitions = 100000000;
  292. #endif
  293.   print_test(items, n_items, repetitions);
  294. #ifdef WITH_SHEW
  295.   test_shewchuk(items, n_items, repetitions);
  296. #endif
  297. #ifdef WITH_KAHAN
  298.   test_kahan(items, n_items, repetitions);
  299. #endif
  300. #ifdef WITH_DEVNULL
  301.   test_devnull(items, n_items);
  302. #endif
  303.   return 0;
  304. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement