homer512 icon

Schewchuk float

homer512 | PRO | 05/05/15 06:11:07 AM UTC | 0 ⭐ | 824 👁️ | Never ⏰ | []
C |

5.48 KB

|

None

|

0 👍

/

0 👎

/*
 * Copyright 2013-2015 Florian Philipp
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 
#include <stdio.h>
/* using printf */
#include <stdlib.h>
/* using malloc, free */
#include <math.h>
/* using fabsf */
 
 
/**
 * Shewchuk summation algorithm.
 *
 * Derived from msum here:
 * http://code.activestate.com/recipes/393090/
 */
struct Accumulator
{
  /**
   * Number of currently used partial sums
   */
  size_t partials_n;
  /**
   * Capacity for partial sums
 
   * partials_cap == 1 indicates use of partials.scalar
   */
  size_t partials_cap;
  /**
   * Partial sums ordered by value
   *
   * To avoid dynamic allocation in the common case that only one sum is kept,
   * the scalar union member is used.
   * The additional if/else comparisons have negligible effect on the run time
   */
  union { float* array; float scalar; } partials;
};
 
 
/**
 * Initializes an Accumulator
 *
 * An Accumulator may only be initialized once to avoid memory leaks
 * unless it is destroyed between initializations.
 * See also: accumulator_reset
 */
void accumulator_init(struct Accumulator* self)
{
  self->partials_n = 0;
  self->partials_cap = 1;
  self->partials.scalar = 0.f;
}
 
/**
 * Releases all resources associated with this Accumulator
 *
 * May only be called once to avoid segfaults.
 * A destroyed Accumulator may only be used after calling accumulator_init.
 * As a rule of thumb, every accumulator_init should be paired with an
 * accumulator_destroy
 */
void accumulator_destroy(struct Accumulator* self)
{
  if(self->partials_cap > 1)
    free(self->partials.array);
}
 
/**
 * Re-initializes an Accumulator in order to use it for another summation
 */
void accumulator_reset(struct Accumulator* self)
{
  self->partials_n = 0;
  if(self->partials_cap == 1)
    self->partials.scalar = 0.f;
}
 
/**
 * Private method of Accumulator. Updates existing partial sums
 *
 * \param self an initialized Accumulator
 * \param x a value to be added
 * \param tail output argument. Returns the index of the last used partial sum.
 * Is less or equal to self->partials_n
 *
 * \return the last partial sum. Has to be stored in self->partials[tail]
 */
static float _accumulator_update_partials(struct Accumulator* self,
                      float x,
                      size_t* tail)
{
  *tail = 0;
  float* partials;
  if(self->partials_cap > 1)
    partials = self->partials.array;
  else
    partials = &self->partials.scalar;
  size_t partial_i;
  for(partial_i = 0; partial_i < self->partials_n; ++partial_i) {
    float y = partials[partial_i];
    if(fabsf(x) < fabsf(y)) {
      float tmp = y;
      y = x;
      x = tmp;
    }
    float hi = x + y;
    float lo = y - (hi - x);
    if(lo != 0.f) {
      partials[*tail] = lo;
      *tail += 1;
    }
    x = hi;
  }
  return x;
}
 
/**
 * Private method of Accumulator. Extends array of partial sums if required
 *
 * Updates self->partials_cap and self->partials.
 * May move data from self->partials.scalar to self->partials.array
 *
 * \param self an initialized Accumulator
 * \param tail the last index that has to be used
 *
 * \return 0 on success. 1 if memory allocation failed
 */
static int _accumulator_reserve(struct Accumulator* self, size_t tail)
{
  if(tail < self->partials_cap)
    return 0;
  size_t reserved = (tail + 1) * 2;
  float* partials_ext;
  if(self->partials_cap == 1) {
    partials_ext = malloc(reserved * sizeof(float));
    if(! partials_ext)
      return 1;
    partials_ext[0] = self->partials.scalar;
  }
  else {
    partials_ext = realloc(self->partials.array, reserved * sizeof(float));
    if(! partials_ext)
      return 1;
  }
  self->partials.array = partials_ext;
  self->partials_cap = reserved;
  return 0;
}
 
/**
 * Adds a value to the accumulated sum
 *
 * \return 0 on success. 1 if resource allocation failed
 */
int accumulator_add(struct Accumulator* self, float x)
{
  size_t tail;
  x = _accumulator_update_partials(self, x, &tail);
  if(_accumulator_reserve(self, tail))
    return 1;
  self->partials_n = tail + 1;
  if(self->partials_cap > 1)
    self->partials.array[tail] = x;
  else
    self->partials.scalar = x;
  return 0;
}
 
/**
 * Returns the sum of all added values
 *
 * Note that this call has some overhead. Results should be cached
 */
float accumulator_sum(const struct Accumulator* self)
{
  if(self->partials_cap == 1)
    return self->partials.scalar;
  float sum = 0.f;
  size_t i;
  for(i = 0; i < self->partials_n; ++i)
    sum += self->partials.array[i];
  return sum;
}
 
int main(void)
{
  float a = 1.0f;
  float b = 100000000.f;
  float c = -100000000.f;
  double d,e,f;
  struct Accumulator sum;
 
  d = (a + b) + c;
  e = a + (b + c);
  accumulator_init(&sum);
  accumulator_add(&sum, a);
  accumulator_add(&sum, b);
  accumulator_add(&sum, c);
  f = accumulator_sum(&sum);
  accumulator_destroy(&sum);
  printf("d=%20.20lf e=%20.20lf, f=%20.20lf\n",d, e, f);
  return 0;
}

Comments