Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4471414

Arrays.binarySearch(float[]) do not distinguish various NaN

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Fixed
    • Icon: P3 P3
    • 1.4.0
    • 1.4.0
    • core-libs
    • beta2
    • generic
    • generic
    • Verified



      Name: bsC130419 Date: 06/18/2001


      java version "1.4.0-beta"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta-b65)
      Java HotSpot(TM) Client VM (build 1.4.0-beta-b65, mixed mode)

      Arrays.binarySearch(float[])'s implementation makes use of Float.floatToIntBits
      (float), which collapse all NaN values into a single canonical one.
      Consequently, when searching a specific NaN number, Arrays.binarySearch(float
      []) find a random NaN number. The problem is shown by the following test case:




      import java.util.Arrays;

      public class Test
      {
          private static void print(float value)
          {
              System.out.print(value);
              System.out.print(" (");
              System.out.print(Integer.toHexString(Float.floatToRawIntBits(value)));
              System.out.print(')');
          }

          public static void main(String[] args)
          {
              // Note: this array fully respect the "Float.compare"
              // ordering: NaN values are last.
              float[] data =
              {
                  0.4f,
                  8.7f,
                  9.3f,
                  Float.intBitsToFloat(0x7fc00001), // NaN
                  Float.intBitsToFloat(0x7fc00002), // NaN
                  Float.intBitsToFloat(0x7fc00003), // NaN
                  Float.intBitsToFloat(0x7fc00004) // NaN
              };
              for (int i=0; i<data.length; i++)
              {
                  System.out.print("search ");
                  print(data[i]);
                  final int index = Arrays.binarySearch(data, data[i]);
                  System.out.print(", found ");
                  print(data[index]);
                  System.out.println();
              }
          }
      }

      --------------------------
      % java Test
      search 0.4 (3ecccccd), found 0.4 (3ecccccd)
      search 8.7 (410b3333), found 8.7 (410b3333)
      search 9.3 (4114cccd), found 9.3 (4114cccd)
      search NaN (7fc00001), found NaN (7fc00001)
      search NaN (7fc00002), found NaN (7fc00001)
      search NaN (7fc00003), found NaN (7fc00001)
      search NaN (7fc00004), found NaN (7fc00001)
      %
      --------------------------


      The test case show that Arrays.binarySearch(float[]) do not find the NaN value
      we requested. It may be possible to fix the problem with minimal risk. There is
      the Arrays.binarySearch(float[]) source code:



      private static int binarySearch(float[] a, float key, int low,int high) {
          while (low <= high) {
              int mid =(low + high)/2;
              float midVal = a[mid];

              int cmp;
              if (midVal < key) {
                  cmp = -1; // Neither val is NaN, thisVal is smaller
              } else if (midVal > key) {
                  cmp = 1; // Neither val is NaN, thisVal is larger
              } else {
                  int midBits = Float.floatToIntBits(midVal);
                  int keyBits = Float.floatToIntBits(key);
                  cmp = (midBits == keyBits ? 0 : // Values are equal
                         (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
                          1)); // (0.0, -0.0) or (NaN, !NaN)
              }

              if (cmp < 0)
                  low = mid + 1;
              else if (cmp > 0)
                  high = mid - 1;
              else
                  return mid; // key found
          }
          return -(low + 1); // key not found.
      }





        Suggested fix: Replace the two Float.floatToIntBits(float) calls by
      Float.floatToRawIntBits(float). In order to preserve backward compatibility
      with existing programs (since current behavior never return an insertion point
      between two NaN values), add a new line before the last line:



      if (Float.isNaN(key) && low<a.length) return low; // For backward compatibility
      return -(low+1);



      The suggested fix will make absolutely no difference when using
      Arrays.binarySearch(float[]) with only real numbers, infinities and the
      canonical Float.NaN value, which cover most cases. If the array contains a set
      of different NaN values, then there are two possible cases:


      Case 1) NaN values are sorted in increasing order (as returned by
      Float.floatToRawIntBits(float)). In this case, Arrays.binarySearch(float[])
      will work as expected. However, because of last lines change,
      Arrays.binarySearch(float[]) will returns a positive index even if no exact
      match is found for a NaN value. This is necessary for backward compatibility.

      Case 2) NaN values are not sorted in increasing order, because legacy code was
      not multi-NaN aware. In this case, Arrays.binarySearch(float[]) will returns a
      random NaN number, which is quite similar to the current behavior. The only
      difference is that it may not be the same random NaN than before. In both case,
      the specification is not violated and compatibility with existing code should
      not be broken.
      (Review ID: 126773)
      ======================================================================

            darcy Joe Darcy
            bstrathesunw Bill Strathearn (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: