Re: Turn a float into a value between 0 and 1 (inclusive)?

2017-11-21 Thread Biotronic via Digitalmars-d-learn

On Tuesday, 21 November 2017 at 09:21:29 UTC, Chirs Forest wrote:
I'm interpolating some values and I need to make an 
(elapsed_time/duration) value a float between 0 and 1 
(inclusive of 0 and 1). The elapsed_time might be more than the 
duration, and in some cases might be 0 or less. What's the most 
efficient way to cap out of bounds values to 0 and 1? I can do 
a check and cap them manually, but if I'm doing a lot of these 
operations I'd like to pick the least resource intensive way.


Good old comparisons should be plenty in this case:

T clamp(T)(T value, T min, T max) {
return value < min ? min : value > max ? max : value;
}

That's two comparisons and two conditional moves. You're not 
gonna beat that.


Also, if I wanted out of bounds values to wrap (2.5 becomes 
0.5) how would I get that value and also have 1.0 not give me 
0.0?


As Petar points out, this is somewhat problematic. You think of 
the codomain (set of possible results) as being the size (1.0 - 
0.0) = 1.0. However, since you include 1.0 in the set, the size 
is actually 1.0 + ε, which is very slightly larger. You could use 
a slightly modified version of Petar's function:


T zeroToOne(T)(T val) { return val % (1+T.epsilon); }

However, this induces rounding errors on larger numbers. e.g. 1.0 
+ ε becomes 0, while you'd want it to be ε.


I guess I should ask for some clarification - what would you 
expect the return value to be for 2.0? Is that 0.0 or 1.0? How 
about for -1.0?


A simple function that does give the results you want between 0.0 
and 1.0 (inclusive) is this:


T zeroToOne(T)(T val) {
if (val >= 0 && val <= 1.0) {
return val;
}
return val % 1;
}

The problem now, however, is that zeroToOne gives negative 
results for negative values. This is probably not what you want. 
This can be fixed with std.math.signbit:


T zeroToOne(T)(T val) {
import std.math : signbit;
if (val >= 0 && val <= 1.0) {
return val;
}
return (val % 1) + val.signbit;
}

If you're willing to give up the last 1/8388608th of precision 
(for float, 1/4503599627370496th for double) that including 1.0 
gives you, this can become:


T zeroToOne(T)(T val) {
import std.math : signbit;
return (val % 1) + val.signbit;
}

Which is easier to read, and consistent for values outside the 
[0,1) interval.


--
  Biotronic


Re: Turn a float into a value between 0 and 1 (inclusive)?

2017-11-21 Thread Petar via Digitalmars-d-learn
On Tuesday, 21 November 2017 at 09:53:33 UTC, Petar Kirov 
[ZombineDev] wrote:
On Tuesday, 21 November 2017 at 09:21:29 UTC, Chirs Forest 
wrote:
I'm interpolating some values and I need to make an 
(elapsed_time/duration) value a float between 0 and 1 
(inclusive of 0 and 1). The elapsed_time might be more than 
the duration, and in some cases might be 0 or less. What's the 
most efficient way to cap out of bounds values to 0 and 1? I 
can do a check and cap them manually, but if I'm doing a lot 
of these operations I'd like to pick the least resource 
intensive way.


Also, if I wanted out of bounds values to wrap (2.5 becomes 
0.5) how would I get that value and also have 1.0 not give me 
0.0?


Is this what you're looking for:
https://www.desmos.com/calculator/8iytgsr3y3

In which case that would be simply:
T zeroToOne(T)(T val) { return val % 1; }


The problem, as you can see in the plot is that 0.9(9) remains 
0.9(9), however 1.0 and 1.001 become 0.0 and 0.001 
respectively and there's no way around this due to the inherent 
discontinuity of the function.


Re: Turn a float into a value between 0 and 1 (inclusive)?

2017-11-21 Thread Petar via Digitalmars-d-learn

On Tuesday, 21 November 2017 at 09:21:29 UTC, Chirs Forest wrote:
I'm interpolating some values and I need to make an 
(elapsed_time/duration) value a float between 0 and 1 
(inclusive of 0 and 1). The elapsed_time might be more than the 
duration, and in some cases might be 0 or less. What's the most 
efficient way to cap out of bounds values to 0 and 1? I can do 
a check and cap them manually, but if I'm doing a lot of these 
operations I'd like to pick the least resource intensive way.


Also, if I wanted out of bounds values to wrap (2.5 becomes 
0.5) how would I get that value and also have 1.0 not give me 
0.0?


Is this what you're looking for:
https://www.desmos.com/calculator/8iytgsr3y3

In which case that would be simply:
T zeroToOne(T)(T val) { return val % 1; }


Turn a float into a value between 0 and 1 (inclusive)?

2017-11-21 Thread Chirs Forest via Digitalmars-d-learn
I'm interpolating some values and I need to make an 
(elapsed_time/duration) value a float between 0 and 1 (inclusive 
of 0 and 1). The elapsed_time might be more than the duration, 
and in some cases might be 0 or less. What's the most efficient 
way to cap out of bounds values to 0 and 1? I can do a check and 
cap them manually, but if I'm doing a lot of these operations I'd 
like to pick the least resource intensive way.


Also, if I wanted out of bounds values to wrap (2.5 becomes 0.5) 
how would I get that value and also have 1.0 not give me 0.0?