Skip to content

Commit

Permalink
More lerp tests, altering lerp docs
Browse files Browse the repository at this point in the history
  • Loading branch information
clarfonthey committed Jun 13, 2021
1 parent 0865acd commit d8e247e
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 26 deletions.
34 changes: 21 additions & 13 deletions library/std/src/f32.rs
Expand Up @@ -879,19 +879,27 @@ impl f32 {

/// Linear interpolation between `start` and `end`.
///
/// This enables the calculation of a "smooth" transition between `start` and `end`,
/// where start is represented by `self == 0.0` and `end` is represented by `self == 1.0`.
///
/// Values below 0.0 or above 1.0 are allowed, and in general this function closely
/// resembles the value of `start + self * (end - start)`, plus additional guarantees.
///
/// Those guarantees are, assuming that all values are [`finite`]:
///
/// * The value at 0.0 is always `start` and the value at 1.0 is always `end` (exactness)
/// * If `start == end`, the value at any point will always be `start == end` (consistency)
/// * The values will always move in the direction from `start` to `end` (monotonicity)
///
/// [`finite`]: #method.is_finite
/// This enables linear interpolation between `start` and `end`, where start is represented by
/// `self == 0.0` and `end` is represented by `self == 1.0`. This is the basis of all
/// "transition", "easing", or "step" functions; if you change `self` from 0.0 to 1.0
/// at a given rate, the result will change from `start` to `end` at a similar rate.
///
/// Values below 0.0 or above 1.0 are allowed, allowing you to extrapolate values outside the
/// range from `start` to `end`. This also is useful for transition functions which might
/// move slightly past the end or start for a desired effect. Mathematically, the values
/// returned are equivalent to `start + self * (end - start)`, although we make a few specific
/// guarantees that are useful specifically to linear interpolation.
///
/// These guarantees are:
///
/// * If `start` and `end` are [finite], the value at 0.0 is always `start` and the
/// value at 1.0 is always `end`. (exactness)
/// * If `start` and `end` are [finite], the values will always move in the direction from
/// `start` to `end` (monotonicity)
/// * If `self` is [finite] and `start == end`, the value at any point will always be
/// `start == end`. (consistency)
///
/// [finite]: #method.is_finite
#[must_use = "method returns a new number and does not mutate the original value"]
#[unstable(feature = "float_interpolation", issue = "71015")]
pub fn lerp(self, start: f32, end: f32) -> f32 {
Expand Down
42 changes: 42 additions & 0 deletions library/std/src/f32/tests.rs
Expand Up @@ -760,8 +760,11 @@ fn test_total_cmp() {

#[test]
fn test_lerp_exact() {
// simple values
assert_eq!(f32::lerp(0.0, 2.0, 4.0), 2.0);
assert_eq!(f32::lerp(1.0, 2.0, 4.0), 4.0);

// boundary values
assert_eq!(f32::lerp(0.0, f32::MIN, f32::MAX), f32::MIN);
assert_eq!(f32::lerp(1.0, f32::MIN, f32::MAX), f32::MAX);
}
Expand All @@ -770,11 +773,50 @@ fn test_lerp_exact() {
fn test_lerp_consistent() {
assert_eq!(f32::lerp(f32::MAX, f32::MIN, f32::MIN), f32::MIN);
assert_eq!(f32::lerp(f32::MIN, f32::MAX, f32::MAX), f32::MAX);

// as long as t is finite, a/b can be infinite
assert_eq!(f32::lerp(f32::MAX, f32::NEG_INFINITY, f32::NEG_INFINITY), f32::NEG_INFINITY);
assert_eq!(f32::lerp(f32::MIN, f32::INFINITY, f32::INFINITY), f32::INFINITY);
}

#[test]
fn test_lerp_nan_infinite() {
// non-finite t is not NaN if a/b different
assert!(!f32::lerp(f32::INFINITY, f32::MIN, f32::MAX).is_nan());
assert!(!f32::lerp(f32::NEG_INFINITY, f32::MIN, f32::MAX).is_nan());
}

#[test]
fn test_lerp_values() {
// just a few basic values
assert_eq!(f32::lerp(0.25, 1.0, 2.0), 1.25);
assert_eq!(f32::lerp(0.50, 1.0, 2.0), 1.50);
assert_eq!(f32::lerp(0.75, 1.0, 2.0), 1.75);
}

#[test]
fn test_lerp_monotonic() {
// near 0
let below_zero = f32::lerp(-f32::EPSILON, f32::MIN, f32::MAX);
let zero = f32::lerp(0.0, f32::MIN, f32::MAX);
let above_zero = f32::lerp(f32::EPSILON, f32::MIN, f32::MAX);
assert!(below_zero <= zero);
assert!(zero <= above_zero);
assert!(below_zero <= above_zero);

// near 0.5
let below_half = f32::lerp(0.5 - f32::EPSILON, f32::MIN, f32::MAX);
let half = f32::lerp(0.5, f32::MIN, f32::MAX);
let above_half = f32::lerp(0.5 + f32::EPSILON, f32::MIN, f32::MAX);
assert!(below_half <= half);
assert!(half <= above_half);
assert!(below_half <= above_half);

// near 1
let below_one = f32::lerp(1.0 - f32::EPSILON, f32::MIN, f32::MAX);
let one = f32::lerp(1.0, f32::MIN, f32::MAX);
let above_one = f32::lerp(1.0 + f32::EPSILON, f32::MIN, f32::MAX);
assert!(below_one <= one);
assert!(one <= above_one);
assert!(below_one <= above_one);
}
34 changes: 21 additions & 13 deletions library/std/src/f64.rs
Expand Up @@ -881,19 +881,27 @@ impl f64 {

/// Linear interpolation between `start` and `end`.
///
/// This enables the calculation of a "smooth" transition between `start` and `end`,
/// where start is represented by `self == 0.0` and `end` is represented by `self == 1.0`.
///
/// Values below 0.0 or above 1.0 are allowed, and in general this function closely
/// resembles the value of `start + self * (end - start)`, plus additional guarantees.
///
/// Those guarantees are, assuming that all values are [`finite`]:
///
/// * The value at 0.0 is always `start` and the value at 1.0 is always `end` (exactness)
/// * If `start == end`, the value at any point will always be `start == end` (consistency)
/// * The values will always move in the direction from `start` to `end` (monotonicity)
///
/// [`finite`]: #method.is_finite
/// This enables linear interpolation between `start` and `end`, where start is represented by
/// `self == 0.0` and `end` is represented by `self == 1.0`. This is the basis of all
/// "transition", "easing", or "step" functions; if you change `self` from 0.0 to 1.0
/// at a given rate, the result will change from `start` to `end` at a similar rate.
///
/// Values below 0.0 or above 1.0 are allowed, allowing you to extrapolate values outside the
/// range from `start` to `end`. This also is useful for transition functions which might
/// move slightly past the end or start for a desired effect. Mathematically, the values
/// returned are equivalent to `start + self * (end - start)`, although we make a few specific
/// guarantees that are useful specifically to linear interpolation.
///
/// These guarantees are:
///
/// * If `start` and `end` are [finite], the value at 0.0 is always `start` and the
/// value at 1.0 is always `end`. (exactness)
/// * If `start` and `end` are [finite], the values will always move in the direction from
/// `start` to `end` (monotonicity)
/// * If `self` is [finite] and `start == end`, the value at any point will always be
/// `start == end`. (consistency)
///
/// [finite]: #method.is_finite
#[must_use = "method returns a new number and does not mutate the original value"]
#[unstable(feature = "float_interpolation", issue = "71015")]
pub fn lerp(self, start: f64, end: f64) -> f64 {
Expand Down
34 changes: 34 additions & 0 deletions library/std/src/f64/tests.rs
Expand Up @@ -756,8 +756,11 @@ fn test_total_cmp() {

#[test]
fn test_lerp_exact() {
// simple values
assert_eq!(f64::lerp(0.0, 2.0, 4.0), 2.0);
assert_eq!(f64::lerp(1.0, 2.0, 4.0), 4.0);

// boundary values
assert_eq!(f64::lerp(0.0, f64::MIN, f64::MAX), f64::MIN);
assert_eq!(f64::lerp(1.0, f64::MIN, f64::MAX), f64::MAX);
}
Expand All @@ -766,11 +769,42 @@ fn test_lerp_exact() {
fn test_lerp_consistent() {
assert_eq!(f64::lerp(f64::MAX, f64::MIN, f64::MIN), f64::MIN);
assert_eq!(f64::lerp(f64::MIN, f64::MAX, f64::MAX), f64::MAX);

// as long as t is finite, a/b can be infinite
assert_eq!(f64::lerp(f64::MAX, f64::NEG_INFINITY, f64::NEG_INFINITY), f64::NEG_INFINITY);
assert_eq!(f64::lerp(f64::MIN, f64::INFINITY, f64::INFINITY), f64::INFINITY);
}

#[test]
fn test_lerp_nan_infinite() {
// non-finite t is not NaN if a/b different
assert!(!f64::lerp(f64::INFINITY, f64::MIN, f64::MAX).is_nan());
assert!(!f64::lerp(f64::NEG_INFINITY, f64::MIN, f64::MAX).is_nan());
}

#[test]
fn test_lerp_values() {
// just a few basic values
assert_eq!(f64::lerp(0.25, 1.0, 2.0), 1.25);
assert_eq!(f64::lerp(0.50, 1.0, 2.0), 1.50);
assert_eq!(f64::lerp(0.75, 1.0, 2.0), 1.75);
}

#[test]
fn test_lerp_monotonic() {
// near 0
let below_zero = f64::lerp(-f64::EPSILON, f64::MIN, f64::MAX);
let zero = f64::lerp(0.0, f64::MIN, f64::MAX);
let above_zero = f64::lerp(f64::EPSILON, f64::MIN, f64::MAX);
assert!(below_zero <= zero);
assert!(zero <= above_zero);
assert!(below_zero <= above_zero);

// near 1
let below_one = f64::lerp(1.0 - f64::EPSILON, f64::MIN, f64::MAX);
let one = f64::lerp(1.0, f64::MIN, f64::MAX);
let above_one = f64::lerp(1.0 + f64::EPSILON, f64::MIN, f64::MAX);
assert!(below_one <= one);
assert!(one <= above_one);
assert!(below_one <= above_one);
}

0 comments on commit d8e247e

Please sign in to comment.