lerp() produces incorrect values?

Hello! I am using the lerp() function to interpolate between two values, but when printing the final value one the screen it is different from what it was supposed to be. Is there any logical explanation to this, or am i doing something else wrong?

Answers

  • Can you provide a small example that demonstrates exactly what you're talking about?

  • Yeah, sure. I'm building a budget visualization tool and when passing from one year to another, I want the values not to go directly in the new ones, but interpolate smoothly until their equal to the target value. So I'm using the code:

    value = lerp(value, target, 0.1);

    in the function that updates the value, while passing as an argument to the function the value for the next year (named as target). But at the end of the interpolation, the new value of the 'value' variable is not equal to what I expected it to be.

  • edited March 2014

    I meant, can you give us a full piece of code (not your whole project, just enough to show us what you mean) that we can copy and paste onto our own machines? For example, I have no idea what you're passing in for value or target, what you expect value to be, or how many times you're calling this code.

    In other words, I'm asking for an MCVE.

  • edited March 2014

    There:

    int v1, v2, newvalue;
    
    int s;
    
    void setup(){
    
      size(200, 200);
    
      v1=151515151;
    
      v2=242424242;
    
      newvalue=v1;
    
      textAlign(CENTER);
    
    }
    
    void draw(){
    
      background(255);
    
      fill(0);
    
      text(s, width/2, height/2);
    
      update();
    
    }
    
    void update(){
    
      s=int(lerp(s, newvalue, 0.1));
    
    }
    
    void keyPressed(){
    
      if(newvalue==v1){
    
        newvalue=v2;
    
      }
    
      else if(newvalue==v2){
    
        newvalue=v1;
    
      }
    
    }
    

    Isn't it supposed that s should change between 151515151 and 242424242? It actually prints out values that are close to those, but not the exact ones. Maybe I've understood the lerp() function wrong. Is there any other way to go from one value to the other gradually and not by giving the new value directly?

  • edited March 2014

    The problem is in how you're using the lerp() function. The lerp() function takes 3 numbers: a start, an end, and a percentage, and returns the number that is that percentage between the start and end.

    For example, lerp(0, 100, .5) returns 50, and lerp(0, 100, .75) returns 75.

    Your first problem is that you're constantly updating the start variable. Calling lerp(50, 100, .5) returns 75. Calling lerp(75, 100, .5) returns 87.5. So you can see that increasing the start variable will decrease the amount that the return value increases.

    The other problem is that you're converting the return value to an int. Eventually, the value returned by the lerp() function will increase by less than 1, and since you're truncating the value, you simply lose that information. That's why your number looks like it stops increasing in value.

    So, here's a small example that does pretty much what your program does, just with friendlier values and less extra stuff:

    int start = 0;
    int end = 100;
    
    void setup() {
      size(200, 200);
      textAlign(CENTER);
    }
    
    void draw() {
    
      background(255);
      fill(0);
    
      text(start, width/2, height/2);
    
      update();
    }
    
    void update() {
      start=int(lerp(start, end, 0.1));
    }
    

    If you run this code, you'll see that the value stops increasing at 91. That's because the next value returned is 91.9, which is truncated back to 91.

    To fix your problems, you should either simply stop converting to an int:

    float start = 0;
    float end = 100;
    
    void setup() {
      size(200, 200);
      textAlign(CENTER);
    }
    
    void draw() {
    
      background(255);
      fill(0);
    
      text(start, width/2, height/2);
    
      update();
    }
    
    void update() {
      start=lerp(start, end, 0.1);
    }
    

    If you run that program, notice how much slower the increase is towards the end. So the other way to fix your problem is to not change start, but change the percentage passed into lerp() instead:

    int start = 0;
    int end = 100;
    float amt = .01;
    
    int value = start;
    
    void setup() {
      size(200, 200);
      textAlign(CENTER);
    
    }
    
    void draw() {
    
      background(255);
      fill(0);
    
      text(value, width/2, height/2);
    
      update();
    }
    
    void update() {
      amt += .01;
    
      if(value < end){
        value=int(lerp(start, end, amt));
      }
    }
    

    If you use this second approach, make sure you watch out for floating point precision errors!

  • I agree with what you said about converting to an int, but it's still not working. The reason why I used such big and not friendly numbers was because I am dealing with such values. You can try the code you wrote with the values I've put as an example, and it will not do as expected.

  • Like I alluded to, you need to be careful of floating point precision errors, which is exactly what you're experiencing.

    To oversimplify, floats can only handle a certain number of significant digits. Anything after those significant digits is ignored.

    Take this code for example:

      float x = 12345678;
      float y = x + 1;
      println(x == y);
    

    This code prints out false, which is exactly what you would expect. However, run this code:

      float x = 123456789;
      float y = x + 1;
      println(x == y);
    

    And you'll see that it prints out true. The x and y variables have too many significant digits, so the increment isn't taken into account when they're compared.

    A similar thing is happening in your code.

    The answer in your case might be to use doubles instead of floats, because doubles have more precision than floats. However, Processing chose to use float over double to favor simplicity and speed over precision, so you'll have to write your own lerp function. Something like this:

    double start = 151515151;
    double end = 242424242;
    
    void setup() {
      size(200, 200);
      textAlign(CENTER);
    }
    
    void draw() {
    
      background(255);
      fill(0);
    
      text((float)start, width/2, height/2);
    
      update();
    }
    
    void update() {
      start=lerp(start, end, 0.1);
    }
    
    double lerp(double start, double end, double amt){
     return start + (end-start)*amt; 
    }
    
  • This one seems to work. Thanks a lot!

Sign In or Register to comment.