2

I'm trying to finding out the relative error with tbe infinite norm with two vectors for the Jacobi method and I obtain an unexpected solution, 1. But the exact value is 24 / 25. Here's my code:

#include <iostream>
#include <math.h>

using namespace std;

double infinite_norm(double b[]) {
    double maximum;
    for(int i = 0; i < sizeof(b); i++) b[i] = fabs(b[i]);
    maximum = b[0];
    for(int i = 1; i < sizeof(b); i++) maximum = fmax(maximum, b[i]);
    return maximum;
}

double *subtract_vectors(double x[], double y[]) {
    for(int k = 0; k <= sizeof(x); k++) x[k] = x[k] - y[k];
    return x;
}

double relative_error(double x[], double y[]) {
    return infinite_norm(subtract_vectors(x, y)) / infinite_norm(x);
}
int main() {
    static double b[] = {6., 25., -11., 15.};
    static double c[] = {1., 1., 1., 1.};
   cout<<relative_error(b, c)<<endl;
   cout<<24. / 25. <<endl;

    return 0;
}

In relative_error function calculates well infinite_norm(subtract_vectors(x, y)) and infinite_norm(x), but not the division.

Tobal
  • 709
  • 2
  • 11
  • 31
  • is *std::vector<>* totally prohibited by your teacher? – JeJo May 23 '18 at 18:08
  • vscode not support c++11 or c++17 – Tobal May 23 '18 at 18:14
  • 3
    in `infinite_norm` `b` is a pointer, so `sizeof(b)` resolves to the size of a pointer in bytes, not the size of whatever is being pointed at. You will have to pass in the size as a parameter or use `std::vector` or `std::array` (or another, less-likely standard library container) because they carry their size with them. – user4581301 May 23 '18 at 18:23
  • vector b = {1.0, 2.0, 3.0} not work with vscode – Tobal May 23 '18 at 18:29
  • VSCode is just an editor, it does not come with a C++ compiler. Any problems you are having are a result of your compiler and/or compile options. C++11 and C++17 source code is still just text, same as C++03, and you can write it just fine with VSCode. – Ben Voigt May 23 '18 at 18:31
  • 2
    Add `-std=c++11` (or the code for a newer standard if you wish) to the command line to enable [list initialization](http://en.cppreference.com/w/cpp/language/list_initialization). – user4581301 May 23 '18 at 18:31

2 Answers2

2
double *subtract_vectors(double x[], double y[]) {
for(int k = 0; k <= sizeof(x); k++) x[k] = x[k] - y[k];
return x;

C++ passes arrays like x[] by reference by default, so your assignment to x[k] changes the array. After calling subtract_vectors(x, y), you are calling the subtracted version every time you call x.

Create a new array in subtract_vectors and return that instead.

Edit: also, use vector, it's not c++11 but standard library from c++98 :)

Edit2: sizeof(x) returns the size of a pointer, not of the array. This is a bug that can be easily avoided by using std::vector, but if you are glued to pure arrays, you should pass a dimension to subtract_vectors:

double *subtract_vectors(double x[], double y[], int const dim) {

Final code

Since passing around dim so much is pretty annoying, I'd actually set a global variable (constexpr int dim = 4 if arrays are required, #define dim 4 if actual pre-c++11 code is necessary), but I've written it this way now, so you'd have to adapt it yourself. Also, what I'd actually, actually do is use vector or std::array wherever possible.

#include <iostream>
#include <math.h>

using namespace std;

double infinite_norm(double b[], int const dim) {
    double maximum;
    for(int i = 0; i < dim; i++) b[i] = fabs(b[i]);
    maximum = b[0];
    for(int i = 1; i < dim; i++) maximum = fmax(maximum, b[i]);
    return maximum;
}

double *subtract_vectors(double x[], double y[], int const dim) {
      double *result = new double[dim];
    for(int k = 0; k < dim; k++) result[k] = x[k] - y[k];
    return result;
}

double relative_error(double x[], double y[], int const dim) {
    return infinite_norm(subtract_vectors(x, y, dim), dim) / infinite_norm(x, dim);
}

int main() {
  int const dimension{4};
  static double b[] = {6., 25., -11., 15.};
  static double c[] = {1., 1., 1., 1.};
  cout<<relative_error(b, c, dimension)<<endl;
  cout<<24. / 25. <<endl;

  return 0;
}
GeckoGeorge
  • 446
  • 1
  • 3
  • 11
  • Can you add the code please? I tried to create a new array but I didn't get the solution, thanks – Tobal May 23 '18 at 18:17
  • I'll get at it, but it may take a couple of minutes, as your use of c-style arrays confuses my c++ sensibilities. BTW, I think your use of sizeof(x) is also a but, since it will return the size of one pointer, not of an array. – GeckoGeorge May 23 '18 at 18:25
  • 2
    @Tobal `k <= sizeof(x);` has three flaws. `sizeof(x)` provides the size of a pointer and once that is fixed `sizeof(x)` returns the size in bytes, not elements. Finally once those are fixed, `<=` will allow `k` to exceed the bounds of `x` by one. `vector` solves the first two, but not the third. Use a range-based for loop to handle the third. – user4581301 May 23 '18 at 18:27
  • @user4581301 good point with the bounds, that one slipped past me – GeckoGeorge May 23 '18 at 18:38
1

I know that this already has an accepted answer but to illustrate a simple point, there is one section of code from the OP that I will focus on.

double *subtract_vectors(double x[], double y[]) {
    for(int k = 0; k <= sizeof(x); k++) x[k] = x[k] - y[k];
    return x;
}

It has to deal with the expression k <= sizeof(x) where x is an array. You are only obtaining the size of a pointer because the fact that when passing arrays into function arguments they end up decaying to pointer behaviors within the function signature when a stack copy is made for the variable x. In order to get the full size of the array one would need to do this instead: sizeof(x) / sizeof(x[0]). This will return to you how many elements that are in the array.

The easiest and quickest way to resolve this bug is by the following:

double *subtract_vectors(double x[], double y[]) {
    for(int k = 0; k <= (sizeof(x) / sizeof(x[0])) ; k++) x[k] = x[k] - y[k];
    return x;
}

You can refer to this answer for how and why sizeof(arr) / sizeof(arr[0]) works from here: How do sizeof(arr) / sizeof(arr[0]) work?

However this only resolves the issue of finding out how many elements are in the array. And since arrays decay to pointers when being passed as function arguments, the accepted answer already describes how to resolve the issue with getting the proper results:

C++ passes arrays like x[] by reference by default, so your assignment to x[k] changes the array. After calling subtract_vectors(x, y), you are calling the subtracted version every time you call x.

Create a new array in subtract_vectors and return that instead.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • 1
    This is not actually true in this case. The trick only works for arrays themselves, but in the functions in this example, the arrays have decayed to pointers. `sizeof(x)/sizeof(x[0])` returns `1`! To reiterate, once you enter any of the functions that take double x[] type arguments, these are now pure pointers to doubles. – GeckoGeorge May 24 '18 at 09:19
  • @GeckoGeorge True; it's been so long since I've worked with `raw arrays` been so use to using standard containers... and for pointers, been use to working with smart pointers... Thank you for bringing that up! – Francis Cugler May 24 '18 at 09:24
  • It would be kind to future readers if you edited your text to reflect that this won't work here, though it's good to know that the possibility is there for other situations. – GeckoGeorge May 24 '18 at 09:36
  • Oh, and BTW, both g++ and clang++ warned me by default about the fact that `sizeof(x)` returns the size of a `double*`, citing `-Wsizeof-array-argument`. I imagine VS would also mention it, though I have no proof. Read the warnings, kids! (to be clear, this would have helped @Tobal with the original sizeof-problem, too) – GeckoGeorge May 24 '18 at 09:39