Reputation: 41
I have a large 2D array of doubles I would like to sort by first column. The array requires dynamic memory allocation as it is large (2GB).
Below is a simplified example of my broken code using random numbers for example purposes.
#include <stdio.h>
#include <stdlib.h>
int Double_Compare_Function ();
int main()
{
srand(time(0));
int p = 0; //property index
int number_properties = 6; // number_properties
long long n = 0; //node index
long long number_nodes = 5; //number of nodes
/* Declare array */
double **properties_array;
properties_array = malloc(number_nodes * sizeof(double*));
for (n=0; n<number_nodes; n++) {
properties_array[n] = malloc(number_properties * sizeof(double));
}
/* Fill array with numbers */
for (n=0; n<number_nodes; n++) {
for (p=0; p<number_properties; p++) {
properties_array[n][p] = rand();
}
}
printf("Initial array...\n");
for (n=0; n<number_nodes; n++) {
printf("%lli: ", n);
for (p=0; p<number_properties; p++){
printf("%.1f ", properties_array[n][p]);
}
printf("\n");
}
/* Sort array */
qsort(properties_array, (int)number_nodes, number_properties*sizeof(double), Double_Compare_Function);
printf("Sorted array...\n");
for (n=0; n<number_nodes; n++) {
printf("%lli: ", n);
for (p=0; p<number_properties; p++){
printf("%.1f ", properties_array[n][p]);
}
printf("\n");
}
return(0);
}
int Double_Compare_Function (const void * a, const void * b) {
if (*(double*)a > *(double*)b) return 1;
else if (*(double*)a < *(double*)b) return -1;
else return 0;
}
The program compiles without error (aside from random number generation warning). It appears to be pointing at memory I don't intend to.
Initial array...
0: 17189.0 13476.0 24803.0 23588.0 9169.0 13351.0
1: 20992.0 15638.0 23138.0 8580.0 32516.0 24064.0
2: 27139.0 23745.0 19237.0 19279.0 19262.0 25303.0
3: 19407.0 24529.0 23675.0 3102.0 23878.0 5831.0
4: 15299.0 3845.0 27278.0 17467.0 28106.0 6918.0
Sorted array...
0: 17189.0 13476.0 24803.0 23588.0 9169.0 13351.0
1: 19262.0 25303.0 13104405306123376000000000000000000000000.0 0.0 32516.0 24064.0
2: 27139.0 23745.0 1361751537953832600000000000000000000000000000000000000000000000000000.0 0.0 20992.0 15638.0
3: 19407.0 24529.0 23675.0 3102.0 23878.0 5831.0
4: 15299.0 3845.0 27278.0 17467.0 28106.0 6918.0
Question answered.
Upvotes: 1
Views: 256
Reputation: 66441
The actual arguments for the comparisons are double**
, not double*
.
(You get a pointer to each element, not the element itself.)
If you want to sort on the first element, use something like this:
int Compare_Function (const void * a, const void * b) {
double* array_a = *(double**)a;
double* array_b = *(double**)b;
return (int) (array_a[0] - array_b[0]);
}
Upvotes: 0
Reputation: 84569
Your problem is you cannot qsort
an object made from pointer-to-pointer as an array because there is no requirement that the allocated address be contiguous in memory. Instead you need to declare and allocate a pointer-to-array of double [number_properties] and allocate for both nodes and properties in a single call to ensure the object is contiguous in memory, e.g.
/* Declare array */
double (*properties_array)[6];
properties_array = malloc(number_nodes * number_properties * sizeof(double));
if (!properties_array) {
perror ("malloc-properties_array");
return 1;
}
(note: that is technically a pointer-to-VLA of double [number_properties] unless an integer constant is used to specify the number_properties
-- which is unclear from the way you declare number_properties
, but then use an integer constant in your code. Just be aware if you choose to use a VLA in your code that feature is an implementation defined feature as of C11)
Now your qsort
compare can be written:
int Double_Compare_Function (const void * a, const void * b) {
if (*(double * const *)a > *(double * const *)b) return 1;
else if (*(double * const *)a < *(double * const *)b) return -1;
else return 0;
}
Example Use/Output
$ ./bin/doublecmp
Initial array...
0: 2058999144.0 1013160096.0 499880968.0 1375376710.0 1398189150.0 579626176.0
1: 35952305.0 349854458.0 1000340925.0 1397136257.0 2028006902.0 877319625.0
2: 579560718.0 745830077.0 766399485.0 1052819099.0 1279742925.0 80594279.0
3: 390212763.0 603717917.0 1542566382.0 654797188.0 957950686.0 807072250.0
4: 1163825233.0 1748173998.0 261624942.0 152991913.0 269595164.0 2130895736.0
Sorted array...
0: 35952305.0 349854458.0 1000340925.0 1397136257.0 2028006902.0 877319625.0
1: 390212763.0 603717917.0 1542566382.0 654797188.0 957950686.0 807072250.0
2: 579560718.0 745830077.0 766399485.0 1052819099.0 1279742925.0 80594279.0
3: 1163825233.0 1748173998.0 261624942.0 152991913.0 269595164.0 2130895736.0
4: 2058999144.0 1013160096.0 499880968.0 1375376710.0 1398189150.0 579626176.0
Shorthand Compare Function
Note, you can also write the same double-compare function as a single return of the result of your conditionals, e.g.
int Double_Compare_Function (const void * a, const void * b)
{
return (*(double * const *)a > *(double * const *)b) -
(*(double * const *)a < *(double * const *)b);
}
Upvotes: 1
Reputation: 141463
You would want to sort the pointers.
Also you are missing #include <time.h>
Below I added the missing include, limited printing to only the first element of the array and limited the rand()
output with % 100
, so the numbers are smaller.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int Double_Compare_Function(const void *, const void *);
int number_properties = 6; // number_properties
int main()
{
srand(time(0));
int p = 0; //property index
long long n = 0; //node index
long long number_nodes = 5; //number of nodes
/* Declare array */
double **properties_array;
properties_array = malloc(number_nodes * sizeof(double*));
for (n=0; n<number_nodes; n++) {
properties_array[n] = malloc(number_properties * sizeof(double));
}
/* Fill array with numbers */
for (n=0; n<number_nodes; n++) {
for (p=0; p<number_properties; p++) {
properties_array[n][p] = rand() % 100;
}
}
printf("Initial array...\n");
for (n=0; n<number_nodes; n++) {
printf("%lli: ", n);
for (p=0; p<1; p++){
printf("%.1f ", properties_array[n][p]);
}
printf("\n");
}
/* Sort array */
qsort(properties_array, number_nodes, sizeof(double*),
Double_Compare_Function);
printf("Sorted array...\n");
for (n=0; n<number_nodes; n++) {
printf("%lli: ", n);
for (p=0; p<1; p++){
printf("%.1f ", properties_array[n][p]);
}
printf("\n");
}
return(0);
}
int Double_Compare_Function (const void * a, const void * b) {
const double * const *z = a;
const double * const *y = b;
// the z is a pointer to an array of doubles
// let's get the first element
const double k = (*z)[0];
const double m = (*y)[0];
return k > m ? 1 : k < m ? -1 : 0;
}
The Double_Compare_Function
receives two pointers that have the same type as properties_array
- it's a double**
. You need to dereference is twice. As each properties_array
elements points to value returned by malloc(number_properties * sizeof(double))
, I wanted to use the [0]
to make it verbose I mean the first element of an array. The (*z)[0]
is just the same as **z
.
Example execution:
Initial array...
0: 3.0
1: 94.0
2: 35.0
3: 24.0
4: 71.0
Sorted array...
0: 3.0
1: 24.0
2: 35.0
3: 71.0
4: 94.0
Upvotes: 1