gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 85a7991 3/3: qsort library: floating point NaN


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 85a7991 3/3: qsort library: floating point NaN values placed in the end
Date: Thu, 25 Jul 2019 13:18:46 -0400 (EDT)

branch: master
commit 85a79914a1f75a17a2ba2a1cbaac64dcae00888a
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    qsort library: floating point NaN values placed in the end
    
    Until now, all the `gal_qsort' functions that worked with floating point
    types simply returned the standard check (like `(ta > tb) - (ta < tb)' for
    an increasing sort). However, if any, or both, of the elements were NaN, it
    would return 0 (meaning equal). This would create an incorrect sorted
    result.
    
    To fix the problem, with this commit a `COMPARE_FLOAT_POSTPROCESS' macro
    has been added in `lib/qsort.c' and all floating point comparison functions
    will use it when the result of the basic comparison is 0.
    
    This bug was reported by Roberto Baena Gallé.
    
    This fixes bug #56671.
---
 NEWS              |  1 +
 doc/gnuastro.texi |  9 +++++++++
 lib/qsort.c       | 57 ++++++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 56 insertions(+), 11 deletions(-)

diff --git a/NEWS b/NEWS
index 98b850b..022d54d 100644
--- a/NEWS
+++ b/NEWS
@@ -147,6 +147,7 @@ See the end of the file for license conditions.
   bug #56641: MakeProfile's center position changes based on precision.
   bug #56635: Update tutorial 3 with bug-fixed NoiseChisel.
   bug #56662: Converting -R to -Wl,-R causes a crash in configure on macOS.
+  bug #56671: Bad sorting with asttable if nan is present.
 
 
 
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index bd49d1c..32a1961 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -29210,6 +29210,7 @@ both polygons have to be sorted in an anti-clock-wise 
manner.
 @node Qsort functions, Permutations, Polygons, Gnuastro library
 @subsection Qsort functions (@file{qsort.h})
 
+@cindex @code{qsort}
 When sorting a dataset is necessary, the C programming language provides
 the @code{qsort} (Quick sort) function. @code{qsort} is a generic function
 which allows you to sort any kind of data structure (not just a single
@@ -29217,6 +29218,14 @@ array of numbers). To define ``greater'' and 
``smaller'' (for sorting),
 @code{qsort} needs another function, even for simple numerical types. The
 functions introduced in this section are to passed onto @code{qsort}.
 
+@cindex NaN
+Note that larger and smaller operators are not defined on NaN
+elements. Therefore, if the input array is a floating point type, and
+contains NaN values, the relevant functions of this section are going to
+put the NaN elements at the end of the list (after the sorted non-NaN
+elements), irrespective of the requested sorting order (increasing or
+decreasing).
+
 The first class of functions below (with @code{TYPE} in their names) can be
 used for sorting a simple numeric array. Just replace @code{TYPE} with the
 dataset's numeric datatype. The second set of functions can be used to sort
diff --git a/lib/qsort.c b/lib/qsort.c
index 3bba8c6..20aa253 100644
--- a/lib/qsort.c
+++ b/lib/qsort.c
@@ -22,6 +22,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 **********************************************************************/
 #include <config.h>
 
+#include <math.h>
 #include <stdlib.h>
 #include <stdint.h>
 
@@ -30,6 +31,31 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/qsort.h>
 
 
+/*****************************************************************/
+/**********                  Macros               ****************/
+/*****************************************************************/
+/* When one or both elements are NaN, the simple comparison, like `(tb >
+   ta) - (tb < ta)', will give 0 (as if the elements are equal). However,
+   some preference has to be given to the NaN element in a comparison,
+   otherwise the output is not going to be reasonable. We also don't want
+   to check NaNs on every comparison (it will slow down the processing).
+
+   So we'll exploit the fact that when there comparison result doesn't
+   equal zero, we don't have any NaNs and this `COMPARE_FLOAT_POSTPROCESS'
+   macro is called only when the comparison gives zero. Being larger or
+   smaller isn't defined for NaNs, so we'll just put them in the end of the
+   sorted list whether it is sorted by decreasing or increasing mode.*/
+#define COMPARE_FLOAT_POSTPROCESS (                                     \
+   isnan(ta) && isnan(tb)                                               \
+   ? 0                                /* Both NaN, define as equal. */  \
+   /* One is NaN, one isn't. */                                         \
+   : ( isnan(ta)                                                        \
+       ? 1                         /* First is NaN, set as smaller. */  \
+       : ( isnan(tb)                                                    \
+           ? -1                    /* Second is NaN, set as larger. */  \
+           : 0 )                      /* None are NaN, set as equal.*/  \
+       )                                                                \
+)
 
 
 
@@ -120,7 +146,6 @@ gal_qsort_uint64_i(const void *a, const void *b)
   return ( *(uint64_t *)a - *(uint64_t *)b );
 }
 
-
 int
 gal_qsort_int64_d(const void *a, const void *b)
 {
@@ -138,7 +163,8 @@ gal_qsort_float32_d(const void *a, const void *b)
 {
   float ta=*(float*)a;
   float tb=*(float*)b;
-  return (tb > ta) - (tb < ta);
+  int out=(tb > ta) - (tb < ta);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -146,7 +172,8 @@ gal_qsort_float32_i(const void *a, const void *b)
 {
   float ta=*(float*)a;
   float tb=*(float*)b;
-  return (ta > tb) - (ta < tb);
+  int out=(ta > tb) - (ta < tb);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -154,7 +181,8 @@ gal_qsort_float64_d(const void *a, const void *b)
 {
   double ta=*(double*)a;
   double tb=*(double*)b;
-  return (tb > ta) - (tb < ta);
+  int out=(tb > ta) - (tb < ta);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -162,7 +190,8 @@ gal_qsort_float64_i(const void *a, const void *b)
 {
   double ta=*(double*)a;
   double tb=*(double*)b;
-  return (ta > tb) - (ta < tb);
+  int out=(ta > tb) - (ta < tb);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 
@@ -323,7 +352,8 @@ gal_qsort_index_single_float32_d(const void *a, const void 
*b)
 {
   float ta=((float *)(gal_qsort_index_single))[ *(size_t *)a ];
   float tb=((float *)(gal_qsort_index_single))[ *(size_t *)b ];
-  return (tb > ta) - (tb < ta);
+  int out=(tb > ta) - (tb < ta);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -331,7 +361,8 @@ gal_qsort_index_single_float32_i(const void *a, const void 
*b)
 {
   float ta=((float *)(gal_qsort_index_single))[ *(size_t *)a ];
   float tb=((float *)(gal_qsort_index_single))[ *(size_t *)b ];
-  return (ta > tb) - (ta < tb);
+  int out=(ta > tb) - (ta < tb);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -339,7 +370,8 @@ gal_qsort_index_single_float64_d(const void *a, const void 
*b)
 {
   double ta=((double *)(gal_qsort_index_single))[ *(size_t *)a ];
   double tb=((double *)(gal_qsort_index_single))[ *(size_t *)b ];
-  return (tb > ta) - (tb < ta);
+  int out=(tb > ta) - (tb < ta);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -347,7 +379,8 @@ gal_qsort_index_single_float64_i(const void *a, const void 
*b)
 {
   double ta=((double *)(gal_qsort_index_single))[ *(size_t *)a ];
   double tb=((double *)(gal_qsort_index_single))[ *(size_t *)b ];
-  return (ta > tb) - (ta < tb);
+  int out=(ta > tb) - (ta < tb);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -362,7 +395,8 @@ gal_qsort_index_multi_d(const void *a, const void *b)
   float tb=B->values[ B->index ];
 
   /* Return the result. */
-  return (tb > ta) - (tb < ta);
+  int out=(tb > ta) - (tb < ta);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }
 
 int
@@ -377,5 +411,6 @@ gal_qsort_index_multi_i(const void *a, const void *b)
   float tb=B->values[ B->index ];
 
   /* Return the result. */
-  return (ta > tb) - (ta < tb);
+  int out=(ta > tb) - (ta < tb);
+  return out ? out : COMPARE_FLOAT_POSTPROCESS;
 }



reply via email to

[Prev in Thread] Current Thread [Next in Thread]