Home > Software design >  How should I fix these garbage values?
How should I fix these garbage values?

Time:01-16

I am an apprentice of C language, trying to learn some stuff here. I had confronted with a problem, and I wish I could have solved it on my own. But, I am a short wrapper and quite prone to procrastination, so I need your good support. As the title explicitly says, I do not know why my topMatches() function is spitting out some random float values, printing out at the beginning and the end of an array of scores[]. Like this,

Output #1
(-0.00000, Lisa Rose),(0.99124, Gene Seymour),(0.92447, Michael Phillips),(0.89341, Claudia Puig),(0.66285, Mick LaSalle),(0.38125, Jack Matthews),(-1.00000, Toby)
Output #2
(107185664961793568883398204719104.00000, Lisa Rose),(0.99124, Gene Seymour),(0.92447, Michael Phillips),(0.89341, Claudia Puig),(0.66285, Mick LaSalle),(0.38125, Jack Matthews),(-1.00000, Toby)
Output #3
(0.99124, Lisa Rose),(0.92447, Gene Seymour),(0.89341, Michael Phillips),(0.66285, Claudia Puig),(0.38125, Mick LaSalle),(-118195603315995709432961818167345152.00000, Jack Matthews),(-1.00000, Toby)
...

The value should be in the range of -1 and 1. I would really appreciate to see your feedback.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

//data types
struct mInfo
{
    char mName[20];
    float rating;
};
struct cInfo
{
    char name[20];
    struct mInfo movi[7];
};
//prototype fxns
typedef double (*sim_fp)(struct cInfo *, const char *, const char *);
double sim_D(struct cInfo *prefs, const char *person1, const char *person2);
void topMatches(sim_fp fp, struct cInfo *prefs, const char *person1, int num);
int cmpFxn (const void * a, const void * b);
void reverseFxn(float arr[], int num);

int main() 
{
    int num = 7;
    struct cInfo critics[num];
  
    critics[0] = (struct cInfo) {"Lisa Rose", {"Lady in the Water", 2.5, "Snakes on a Plane", 3.5, "Just My Luck", 3, "Superman Returns", 3.5, "The Night Listener", 3, "You, Me and Dupree", 2.5}};
    
    critics[1] = (struct cInfo) {"Gene Seymour",{"Lady in the Water", 3, "Snakes on a Plane", 3.5, "Just My Luck", 1.5, "Superman Returns", 5, "The Night Listener", 3, "You, Me and Dupree", 3.5}};
    
    critics[2] = (struct cInfo) {"Michael Phillips",{"Lady in the Water", 2.5, "Snakes on a Plane", 3, "Superman Returns", 3.5, "The Night Listener", 4}};
    
    critics[3] = (struct cInfo) {"Claudia Puig",{"Snakes on a Plane", 3.5, "Just My Luck", 3, "Superman Returns", 4, "The Night Listener", 4.5, "You, Me and Dupree", 2.5}};
    
    critics[4] = (struct cInfo) {"Mick LaSalle",{"Lady in the Water", 3, "Snakes on a Plane", 4, "Just My Luck", 2, "Superman Returns", 3, "The Night Listener", 3, "You, Me and Dupree", 2}};
    
    critics[5] = (struct cInfo) {"Jack Matthews",{"Lady in the Water", 3, "Snakes on a Plane", 4, "Superman Returns", 5, "You, Me and Dupree", 3.5}};
    
    critics[6] = (struct cInfo) {"Toby",{"Snakes on a Plane", 4.5, "Superman Returns", 4, "You, Me and Dupree",1}};
    
    topMatches(sim_D, critics, "Toby", 7);

    return 0;
}

double sim_D(struct cInfo *prefs, const char *person1, const char *person2)
{
    int i=0;
    int x=0;
    int next=0;
    int p1;
    int p2;
    float X = 0;
    float Y = 0;
    float sumSq1 = 0;
    float sumSq2 = 0;
    float pSum = 0;
    float num = 0;
    float den = 0;
    float Pscore = 0;
    int nElements =0;
    
    
    for (i=0;i<7;i  ) {
        if(strcmp(prefs[i].name, person1) ==0)
        {
           p1 = i; 
        }
        else if(strcmp(prefs[i].name, person2) ==0)
        {
            p2 = i;
        }
    }
  
    for (x=0;x<7;x  ) {
        for (next=0;next<7;next  ) 
        {
            if (!prefs[p1].movi[x].rating && !prefs[p2].movi[next].rating);
            else if (strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName) == 0)
            {
                  X  = prefs[p1].movi[x].rating;
                  Y  = prefs[p2].movi[next].rating;
                  sumSq1  = pow(prefs[p1].movi[x].rating,2);
                  sumSq2  = pow(prefs[p2].movi[next].rating,2);
                  pSum  = (prefs[p1].movi[x].rating*prefs[p2].movi[next].rating); 
                  nElements  ;
            }
        }
        next = 0;
    }
  
    num = pSum-(X*Y/nElements);

    den=sqrt((sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements));
    if(den ==0) return -1;

    Pscore = num/den; 

    return Pscore;
}

void topMatches(sim_fp fp, struct cInfo *prefs, const char *person1, int num)
{
    float scores[8];
    char *buf;
    int i=0;

    for (i=0;i<num;i  )
    {
        if(strcmp(person1, prefs[i].name)==0)
        {
          continue;
        }
        scores[i] = fp(prefs, person1, prefs[i].name);
    }
  
  qsort(scores, num, sizeof(float), (*cmpFxn));
  reverseFxn(scores, num);
   printf("\n\n");
        for(i=0;i<num;i  )
        {
          if (i == num-1)
          {
            printf("(%.5f, %s)", scores[num-1], prefs[num-1].name);
          }
          else
          {
            printf("(%.5f, %s),", scores[i], prefs[i].name);
          }
        }
}
void reverseFxn(float arr[], int num)
{
  float scoresTmp[num];
  int j;
        
    for(j=0;j<num;j  )
    {
      scoresTmp[num-1-j] = arr[j];
    }
      for(j=0;j<num;j  )
      {
        arr[j]=scoresTmp[j];
      }
}
int cmpFxn (const void * a, const void * b) 
{
   return ( *(int*)a - *(int*)b );
}

The value of each elements of the scores[] array needs to be within the range of -1 and 1.

CodePudding user response:

At least these issues:

Wrong compare function

qsort() is called with an array of float and cmpFxn, yet cmpFxn() compares int. *1

Need to cast to float. *2

// return (*(int*)a - *(int*)b )
return (*(float*)a > *(float*)b ) - (*(float*)a < *(float*)b );

No not use return (*(float*)a - *(float*)b ) as the float difference may overflow int range or truncate to 0.

Code risks sqrt(some_negative)

Watch out for slight floating point artifacts that result in a negative that mathematically should never be less than 0.

// den=sqrt((sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements));
t = (sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements);
den = t >= 0.0 ? sqrt(t) : 0;

float vs. double

Code is casual about mixing float and double objects and functions. Suggest double only.

For debug purposes, easy enough to use #define float double to temporarily do this.


*1
cmpFxn() is also a weak compare function for int too as it risks int overflow.

// return ( *(int*)a - *(int*)b );
return (*(int*)a > *(int*)b ) - (*(int*)a < *(int*)b );

*2
If not-a-numbers are involved, the compare is more complicated. Let us assume, for now, that is not the case.

CodePudding user response:

  1. Unexpected variable output is a hint that uninitialized data is being used. In struct cInfo you have a member struct mInfo movi[7]; but assign between 3 and 6 of these in main(). By switching from assignment to initialization those records that are not explicit set will be zero initialized. This seems to be sufficient to get consistent output.
int main() {
    struct cInfo critics[] = {
        {"Lisa Rose", {
            {"Lady in the Water", 2.5},
            {"Snakes on a Plane", 3.5},
            {"Just My Luck", 3},
            {"Superman Returns", 3.5},
            {"The Night Listener", 3},
            {"You, Me and Dupree", 2.5}
        }},
        {"Gene Seymour", {
            {"Lady in the Water", 3},
            {"Snakes on a Plane", 3.5},
            {"Just My Luck", 1.5},
            {"Superman Returns", 5},
            {"The Night Listener", 3},
            {"You, Me and Dupree", 3.5}
        }},
        {"Michael Phillips", {
            {"Lady in the Water", 2.5},
            {"Snakes on a Plane", 3},
            {"Superman Returns", 3.5},
            {"The Night Listener", 4}
        }},
        {"Claudia Puig", {
            {"Snakes on a Plane", 3.5},
            {"Just My Luck", 3},
            {"Superman Returns", 4},
            {"The Night Listener", 4.5},
            {"You, Me and Dupree", 2.5}
        }},
        {"Mick LaSalle", {
            {"Lady in the Water", 3},
            {"Snakes on a Plane", 4},
            {"Just My Luck", 2},
            {"Superman Returns", 3},
            {"The Night Listener", 3},
            {"You, Me and Dupree", 2}
        }},
        {"Jack Matthews", {
            {"Lady in the Water", 3},
            {"Snakes on a Plane", 4},
            {"Superman Returns", 5},
            {"You, Me and Dupree", 3.5}
        }},
        {"Toby", {
            {"Snakes on a Plane", 4.5},
            {"Superman Returns", 4},
            {"You, Me and Dupree",1}
        }}
    };
    topMatches(sim_D, critics, "Toby", sizeof critics / sizeof *critics);
}

and here is example output:

(0.99124, Lisa Rose),(0.92447, Gene Seymour),(0.89341, Michael Phillips),(0.66285, Claudia Puig),(0.38125, Mick LaSalle),(0.00000, Jack Matthews),(-1.00000, Toby)
  1. (not fixed) Consider rewriting the unusual if expression with an empty body:
if (!prefs[p1].movi[x].rating && !prefs[p2].movi[next].rating);
else if (strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName) == 0) {

to:

if (
   (prefs[p1].movi[x].rating || prefs[p2].movi[next].rating) && 
   !strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName)
) {

If you introduce a couple of vanity variables it could be written a more compactly.

  1. Replace float with double and fix the sort function:
int cmpFxn (const void * a, const void * b) {
    double a2 = *(double *) a;
    double b2 = *(double *) b;
    if(a2 < b2) return -1;
    if(a2 > b2) return 1;
    return 0;
}
  1. valgrind now complains about uninitialized values in topMatches specially you double scores[8];. You only iterate over num which is 7, and you don't set scores[i] for the entry that corresponds to person1 so I suggest you do:
    double scores[num];
    memset(scores, 0, sizeof(scores));
  1. (not fixed) Audit your code for magic values, like 7 and 20. Use constants, pass in an argument or sizeof a / sizeof *a if you need the size of an array (be careful arrays degrade to pointers when passed as arguments).
  • Related