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

Add clamp() methods to java.lang.Math and to StrictMath

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 21
    • core-libs
    • None
    • source
    • minimal
    • Hide
      Adding new static methods that don't overload previously existing methods to the final class poses minimal compatibility issue. Source compatibility might be broken if somebody uses `import static java.lang.Math.*;` and imports the `clamp` method from another class using on-demand import. We believe that such a situation appears exceedingly rare. No binary compatibility problems are expected.
      Show
      Adding new static methods that don't overload previously existing methods to the final class poses minimal compatibility issue. Source compatibility might be broken if somebody uses `import static java.lang.Math.*;` and imports the `clamp` method from another class using on-demand import. We believe that such a situation appears exceedingly rare. No binary compatibility problems are expected.
    • Java API
    • SE

      Summary

      Add four new methods named clamp() into java.lang.Math and java.lang.StrictMath classes that allow clamping numerical values to fit the specified range.

      Problem

      Quite often it's necessary to clamp a numerical value to a given range, using the algorithm like this:

      int clampedValue = value > max ? max : value < min ? min : value;

      or probably

      int clampedValue = Math.max(min, Math.min(max, value));

      These "algorithms" are verbose and error-prone. E.g., many years ago Math.max and Math.min were mixed in Jenkins CI implementation. Static analyzers like SonarLint even implement a rule to catch such kind of mistake, but it cannot statically find a mistake if max and min values are not constant.

      Having a library method for this algorithm will make the code more readable, and robust. Standard libraries of various languages tend to implement a function that encapsulates this algorithm. E.g., Kotlin coerseIn, C# Math.Clamp, Rust num::clamp.

      Solution

      It's proposed to add methods to java.lang.Math and to java.lang.StrictMath class to solve this particular problem. We need four overloads to cover int, long, float, and double types. The signatures could be:

      public static int clamp(long value, int min, int max)
      public static long clamp(long value, long min, long max)
      public static double clamp(double value, double min, double max)
      public static float clamp(float value, float min, float max)

      Specification

      In both java.lang.Math and java.lang.StrictMath add:

          /**
           * Clamps the value to fit between min and max. If the value is less 
           * than {@code min}, then {@code min} is returned. If the value is greater
           * than {@code max}, then {@code max} is returned. Otherwise, the original
           * value is returned.
           * <p>
           * While the original value of type long may not fit into the int type,
           * the bounds have the int type, so the result always fits the int type.
           * This allows to use method to safely cast long value to int with 
           * saturation.
           * 
           * @param value value to clamp
           * @param min minimal allowed value
           * @param max maximal allowed value
           * @return a clamped value that fits into {@code min..max} interval
           * @throws IllegalArgumentException if {@code min > max}
           * 
           * @since 21
           */
          public static int clamp(long value, int min, int max) {...}
      
          /**
           * Clamps the value to fit between min and max. If the value is less 
           * than {@code min}, then {@code min} is returned. If the value is greater
           * than {@code max}, then {@code max} is returned. Otherwise, the original
           * value is returned.
           *
           * @param value value to clamp
           * @param min minimal allowed value
           * @param max maximal allowed value
           * @return a clamped value that fits into {@code min..max} interval
           * @throws IllegalArgumentException if {@code min > max}
           * 
           * @since 21
           */
          public static long clamp(long value, long min, long max) {...}
      
          /**
           * Clamps the value to fit between min and max. If the value is less 
           * than {@code min}, then {@code min} is returned. If the value is greater
           * than {@code max}, then {@code max} is returned. Otherwise, the original
           * value is returned. If value is NaN, the result is also NaN.
           * <p>
           * Unlike the numerical comparison operators, this method considers
           * negative zero to be strictly smaller than positive zero. 
           * E.g., {@code clamp(-0.0, 0.0, 1.0)} returns 0.0.
           * 
           * @param value value to clamp
           * @param min minimal allowed value
           * @param max maximal allowed value
           * @return a clamped value that fits into {@code min..max} interval
           * @throws IllegalArgumentException if either of {@code min} and {@code max}
           * arguments is NaN, or {@code min > max}, or {@code min} is +0.0, and 
           * {@code max} is -0.0. 
           *
           * @since 21
           */
          public static double clamp(double value, double min, double max) {...}
      
          /**
           * Clamps the value to fit between min and max. If the value is less 
           * than {@code min}, then {@code min} is returned. If the value is greater
           * than {@code max}, then {@code max} is returned. Otherwise, the original
           * value is returned. If value is NaN, the result is also NaN.
           * <p>
           * Unlike the numerical comparison operators, this method considers
           * negative zero to be strictly smaller than positive zero. 
           * E.g., {@code clamp(-0.0f, 0.0f, 1.0f)} returns 0.0f. 
           *
           * @param value value to clamp
           * @param min minimal allowed value
           * @param max maximal allowed value
           * @return a clamped value that fits into {@code min..max} interval
           * @throws IllegalArgumentException if either of {@code min} and {@code max}
           * arguments is NaN, or {@code min > max}, or {@code min} is +0.0f, and 
           * {@code max} is -0.0f.
           *
           * @since 21
           */
          public static float clamp(float value, float min, float max) {...}

            tvaleev Tagir Valeev
            tvaleev Tagir Valeev
            Joe Darcy
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: