Example

We use the same program for arclength computation as in [RNND2013] with all floating-point variables initially declared in double precision.

Initial code:

double fun(double x){
  int k, n = 5;
  double t1;
  double d1 = 1.0;

  t1 = x;
  for ( k = 1; k <= n; k++ )
    {
      d1 = 2.0 * d1;
      t1 = t1+ sin(d1 * x)/d1;
    }
  return t1;
}

int main( int argc, char **argv) {

  int i,n = 1000000;
  double h;
  double t1, t2, dppi;
  double s1;
  std::ofstream res;
  std::cout.precision(15);

  t1 = -1.0;
  dppi = acos(t1);
  s1 = 0.0;
  t1 = 0.0;
  h = dppi / n;

  for ( i = 1; i <= n; i++)
    {
      t2 = fun(i * h);
      s1 = s1 + sqrt(h*h + (t2 - t1) * (t2 - t1));
      t1 = t2;
      //if (i%1000==0) PROMISE_CHECK_VAR(t1);
    }

  std::cout << s1 << std::endl;

  return 0;
}

Result: 5.79577632241303

Result printed using CADNA: 0.579577632241E+001 (other digits are affected by round-off errors)

Code modified to be used with PROMISE:

To run an example, first you should replace every variable type you want to test by __PROMISE__. Also, if you want to link the types of some variables (for example link the type of arguments between the function calls and their prototypes) those types should be replaced by __PR_XXXX__ (XXXX is user defined). In a second step, you should specify a variable whose accuracy should be checked by PROMISE. For this purpose, if the variable(var) is an array you should insert this kind of instruction after the computation of var:

PROMISE_CHECK_ARRAY(var, size of var);

Otherwise, you need to insert the following instruction:

PROMISE_CHECK_VAR(var);
__PR_fun__ fun(__PR_1__ x){
  int k, n = 5;
  __PR_fun__ t1;
  __PR_1__ d1 = 1.0;

  t1 = x;
  for ( k = 1; k <= n; k++ )
    {
      d1 = 2.0 * d1;
      t1 = t1+ sin(d1 * x)/d1;
    }
  return t1;
}

int main( int argc, char **argv) {

  int i,n = 1000000;
  __PR_1__ h;
  __PROMISE__ t1, t2, dppi;
  __PROMISE__ s1;
  std::ofstream res;
  std::cout.precision(15);

  t1 = -1.0;
  dppi = acos(t1);
  s1 = 0.0;
  t1 = 0.0;
  h = dppi / n;

  for ( i = 1; i <= n; i++)
    {
      t2 = fun(i * h);
      s1 = s1 + sqrt(h*h + (t2 - t1) * (t2 - t1));
      t1 = t2;
      //if (i%1000==0) PROMISE_CHECK_VAR(t1);
    }

  std::cout << s1 << std::endl;
  PROMISE_CHECK_VAR(s1);
  return 0;
}

Now, you can run PROMISE for Half/Single/Double mixed-precision with this command:

runPromise hsd

You can modify the number of requested digits (nbDigits) in the promise.yml file.

Code after the use of PROMISE (6 requested digits):

The modified source code can be found in the directory which has been specified in the output option in the promise.yml file. If this option is not included in the promise.yml, by default, a directory named result will contain the modified source code.

double fun(double x){
  int k, n = 5;
  double t1;
  half_float::half d1; d1= 1.0;

  t1 = x;
  for ( k = 1; k <= n; k++ )
    {
      d1 = 2.0 * d1;
      t1 = t1+ sin(d1 * x)/d1;
    }
  return t1;
}

int main( int argc, char **argv) {

  int i,n = 1000000;
  double h;
  double t1;double t2;float dppi;
  double s1;
  std::ofstream res;
  std::cout.precision(15);

  t1 = -1.0;
  dppi = acos(t1);
  s1 = 0.0;
  t1 = 0.0;
  h = dppi / n;

  for ( i = 1; i <= n; i++)
    {
      t2 = fun(i * h);
      s1 = s1 + sqrt(h*h + (t2 - t1) * (t2 - t1));
      t1 = t2;
      //if (i%1000==0) PROMISE_CHECK_VAR(t1);
    }

  std::cout << s1 << std::endl;

  return 0;
}

Result: 5.79577686259398 (6 correct digits, i.e. in common with the CADNA result)

[RNND2013]
  1. Rubio-González, C. Nguyen, H.D. Nguyen, J. Demmel, W. Kahan, K. Sen, D.H. Bailey, C. Iancu, D. Hough, Precimonious: Tuning assistant for floating-point precision, in: Proceedings of the International Conference on High Performance Computing, Networking, Storage and Analysis, SC ‘13, ACM, New York, NY, USA, 2013, pp. 27:1-27:12.