10

I have been cracking my head at achieving something very simple in C in order to make my one of the programs (not written by me) in our computational physics project more dynamic: comparing two different arrays element by element in an if conditional.

#include <math.h>
#include <stdio.h>
#include "header.h"
const int nParam = 10;
double a[nParam], a_tmp[nParam];
double values[10000];

double FitParam(double x){
    int xindex;
    double value;

    xindex=(int) x;

    if (a_tmp[1]==a[1] && a_tmp[2]==a[2] && a_tmp[3]==a[3] && a_tmp[4]==a[4]){ 
        value=values[xindex];
        return(value);
    }

// code continues... (very long subroutine and there is recursion for
// the subroutine so this if statement above is very important).

The array a[ ] has a varying number of significant elements every time we run our program; for example, right now, we are using this subroutine for only elements [1] through [4]. However, in other cases, we will want to have fewer or more elements, say, up to 3 elements or up to 5 elements, respectively.

So essentially, I want to be able to rewrite the if statement above so that it is dynamic... in other words, if there are N elements considered, then it will do:

if (a_tmp[1]==a[1] && ... && a_tmp[N]==a[N]){}

So this if conditional should vary whenever our number N of elements of interest is changed (N is defined as a #define in the header of this file, which I just named header.h).

I would greatly appreciate your support on this task. Thank you.

Neuron
  • 5,141
  • 5
  • 38
  • 59
LightningXI
  • 103
  • 1
  • 1
  • 5
  • 2
    Comparing `double`s for equality is generally a bad idea. – T.C. Jun 11 '14 at 02:54
  • 3
    I'm inferring that you're intentionally eschewing a loop for "performance reasons" yeah? If so, you should really run a profiler to make sure that your manually expanded solution is actually faster. Even if it is, it helps to quantify the gain that you're chasing. – Pete Baughman Jun 11 '14 at 02:56
  • 1
    `memcmp(&a_tmp[1], &a[1], 4 * sizeof a[1]);` – M.M Jun 11 '14 at 03:18

6 Answers6

17

Your best bet is to rewrite it as a function that returns true or false (1 or 0):

int compareArrays(double a[], double b[], int n) {
  int ii;
  for(ii = 1; ii <= n; ii++) {
    if (a[ii] != b[ii]) return 0;
    // better:
    // if(fabs(a[ii]-b[ii]) < 1e-10 * (fabs(a[ii]) + fabs(b[ii]))) {
    // with the appropriate tolerance
  }
  return 1;
}

Note that it is usually bad practice to compare doubles for equality - you are better off comparing their difference, and making sure the absolute value is less than some tolerance.

Also note you are comparing elements 1 through n - C arrays start at 0 though.

You would use the above with

if (compareArrays(a, a_tmp, N)) {

where the value N is #define'd per your question.

If you want to be "clever" and avoid a loop, you can write the following - it will stop ("short-circuiting") as soon as you reach the right number of comparisons. It is still a Bad Idea to compare doubles for equality but I will leave that for another time (see comment in code above for a solution).

if(a[1]==a_temp[1] && (2 > N || (a[2]==a_temp[2] && (3 > N || (a[3]==a_temp[3]))))) {

This makes the "and the rest" true as soon as you have compared the right number of terms - so it will stop evaluating terms (as you need). I am not convinced this is either faster, or better code - but it is "dynamic"... You can obviously make this expression as long as you would like; I just wrote the first three terms so you get the idea. I DO NOT RECOMMEND IT.

As for the comparison of doubles, you might consider replacing

if(a == b)

with

if(closeEnough(a, b))

where you define the macro

#define closeEnough(a, b) (fabs((a)-(b)) < 1e-10 * (fabs(a) + fabs(b)))? 1 : 0

This will make sure that your doubles don't have to be "exactly equal" - depending on how you arrived at them, they will almost never be, and the relative tolerance of 1 part in 10^10 is usually plenty for most practical comparisons.

Floris
  • 45,857
  • 6
  • 70
  • 122
  • 1
    +1 solid discussion of problems comparing floating point numbers – dwerner Jun 11 '14 at 03:42
  • 1
    Thank you for the thoroughly detailed answer! Should "compareArrays" have as arguments `double a[]` and `double b[]`? Thanks also for the discussion on equality on doubles. – LightningXI Jun 11 '14 at 16:55
  • @LightningXI - you are right, the arguments should be pointers. I will update. – Floris Jun 11 '14 at 17:06
7

If it must be at compile time, there is nothing in the standard that provides for a repeating macro like that. As in another (question), for bounded N, you can prepare N macros that expand to your desired comparison.

While yet another alternative is memcmp

memcmp( data, data2, array_len_in_bytes );

reference

dwerner
  • 6,462
  • 4
  • 30
  • 44
  • 1
    For completeness: note that `arraylen` is array length in _bytes_ not _elements_, and that comparing `doubles` for equality is not a terrific idea; but interesting suggestion to use `memcmp`. – Floris Jun 11 '14 at 03:27
  • I had never considered `memcmp`. I appreciate the solution. – LightningXI Jun 11 '14 at 16:56
2

An implementation might be to loop over all the elements and set a flag when a difference is detected

int i, N;
int is_equal = 1;

for (i=1; i<N; ++i) {
    if (a[i] != a_tmp[i]) {
        is_equal = 0;
        break;
    }
}

if (is_equal)
    printf("Arrays are equal");
Pankrates
  • 3,074
  • 1
  • 22
  • 28
  • 1
    Note that the code in the question seems to compare elements from `[1]` onwards... strange, but there it is. – Floris Jun 11 '14 at 02:55
  • 1
    Good point, but I was merely trying to illustrate the concept instead of providing ready made custom-tailored code. I see we came up with the same concept though :) – Pankrates Jun 11 '14 at 02:57
  • Yes. The `[0]` element is in fact reserved for something else, so the comparison applies from `[1]` onwards. I am sorry for not clarifying that. – LightningXI Jun 11 '14 at 16:56
1

A simple implementation is a linear comparison between both arrays, it just iterate over the array length and check if (a[i] != b[i]), if so return false & break out of the iteration.

See the example below:

#include <stdio.h>

int compareArrays(int a[], int b[], int n)
{
  for (int i=0; i<n; ++i)
  {
      if (a[i] != b[i])
      {
          return -1;
      }
  }
  return 0;
}

int main()
{
    int arr1[4] = {3, 4, 5, 7};
    int arr2[4] = {3, 4, 5, 7};

    int arr3[4] = {1, 5, 3, 7};
    int arr4[4] = {3, 4, 5, 19};

    printf("Should be True %d\n", compareArrays(arr1, arr2, 4));
    printf("Should be False %d\n", compareArrays(arr3, arr4, 4));
    return 0;
}

You should get:

Should be True 0
Should be False -1

Run it online this example: https://repl.it/@abranhe/compare-arrays-in-c

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Abraham
  • 8,525
  • 5
  • 47
  • 53
0

This one, lets you compare two arrays of any type and will return the index of the first unequal elements found. If the arrays are identical the returned value will be the number of elements in the array.

int compareArrays(void* arrayA, void* arrayB, uint numElements, uint elementSizeBytes)  {

    //returns -1 on error, numElememts if the arrays are equal or the index
    //of the first unequal elements

    uint i;
    uint8_t* byteArrayA;
    uint8_t* byteArrayB;

    if(elementSizeBytes < 1)    {
        return -1;
    }

    if(numElements < 1) {
        return -1;
    }

    byteArrayA = (uint8_t*) arrayA;
    byteArrayB = (uint8_t*) arrayB;

    for(i = 0; i < (numElements*elementSizeBytes); i++) {
        if(byteArrayA[i] != byteArrayB[i])  {
            break;
        }
    }

    return i / elementSizeBytes;
}

An example call:

uint16_t test1[6] = {12, 15, 24, 86, 92, 15};

uint16_t test2[6] = {12, 15, 24, 86, 93, 15};

int retVal = compareArrays(test1, test2, 6, 2);
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Paul K
  • 1
-1

Today i came across same kind of problem statement,i googled for solution for an hour and end up with no solution,the above all approaches are not correct solutions for the stated problem

The Better way to resolve above Problem is

Sort the two arrays either in ascending or descending order, Then compare both the arrays.

#include<stdio.h>
 void sort_it(int a[], int size)
      {
       int i,j,temp=0;
       for(i=0;i<size;++i)
        {

        for(j=i+1;j<size;++j)

        {
            if(a[i]>a[j])
                {
                temp=a[i];
                a[i]=a[j];
                a[j]=temp;
                }
        }
    }
};

int compare(int size,int a[],int b[])
{ 
    int i,j,is_equal;
    for(i=0;i<size;i++)
    {
        for(j=0;j<size;j++)`enter code here`
        {
            if(a[i]!=b[j])
            {
            is_equal=0;
            }
            else
            is_equal=1;
        }

    }



return is_equal;
};

int main()
{
    int size=4,i,is_equal;
    int a[]={1,2,5,4};
    int b[]={1,7,4,2};

    sort_it(a,size);
    sort_it(b,size);
    is_equal=compare(4,a,b);
    if(is_equal)
        printf("arrays are equal\n");
    else
        printf("arrays are not equal\n");
    return (0);

}
Lax
  • 21
  • 1
  • 9
  • There is nothing in the problem statement to suggest that elements need to be compared regardless of their order in the array. This might be what you need - but I don't believe it is what OP asked for. – Floris Jul 02 '16 at 22:47