Math.clamp method?

John Rose john.r.rose at oracle.com
Sat Jan 28 16:37:40 UTC 2023


> On Jan 27, 2023, at 2:46 AM, Tagir Valeev <amaembo at gmail.com> wrote:
> 
> …
>> 
>> 
>> 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(double value, float min, float max)
> 
> I really like the idea of int clamp(long value, int min, int max), as
> this makes the method much more useful. On the other hand, I have
> doubles about
> float clamp(double value, float min, float max), as it could be called
> accidentally and we will have accidental precision loss. E.g., imagine
> the following code:

That was a typo. Oops, sorry. All I am trying to suggest is to widen both the first argument and the return value to long or double. 
> 
> // bounds are declared as floats for some reason
> // this usually doesn't matter, as these numbers are exact in float
> and in double
> float lower = 0.0f;
> float upper = 10.0f;
> 
> double v = 1/3.;
> System.out.println(v);
> v = Math.clamp(v, lower, upper); // oops!
> System.out.println(v);
> 
> Having float clamp(double, float, float), this method will be linked
> here, resulting in accidental precision loss. The output is:
> 0.3333333333333333
> 0.3333333432674408
> 
> Given that float type is rarely used, I'd stick to the following signatures:
> 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)
> 
>> I thought about cross-domain clamps, which preserve 64 bit
>> precision and double range at the same time in different places:
>> 
>> public static long clamp(long value, double min, double max)
>> public static long clamp(double value, long min, long max)
> 
> This is even more dangerous, as simply calling
> double value = ...
> value = clamp(value, 0, 1); // not 0.0 and 1.0
> you will get 0 instead of the original value that fits the range.
> I imagine that many people will step on this. If these methods are desired,
> it would be better to name them differently, to avoid overload ambiguity.
> For now, I would avoid this.

Of course. Widen the return if you widen the first argument. Then it works out. 
> 
>> The subexpression max-min never overflows, when it
>> is regarded as an unsigned number, and the “>>>1”
>> immediately converts that unsigned number into the
>> correctly signed positive number. That’s the only
>> way to avoid overflow for all inputs. The third
>> idiom above (the “smartest” of the three) will fail,
>> for example, if you feed it negative values.
>> For some uses that is OK, but for others it isn’t.
>> 
>> public static int midpoint(int min, int max)
>> public static long midpoint(long min, long max)
>> public static double midpoint(double min, double max)
>> public static float midpoint(float min, float max)
> 
> midpoint is another great idea, I like it! Could also be considered separately.
> 
> With best regards,
> Tagir Valeev.


More information about the core-libs-dev mailing list