0

I have a problem with a script, I don't understand what's going wrond.

Here it is :

let angle = 0
let multiplier = .01

function raf() {

  if(angle >= 1 || angle < 0) {
    multiplier = - multiplier 
  }

  angle = angle + multiplier

  if(angle < 0) console.log(angle)

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

The purpose is the angle to raise by 0.01 and when it reaches 1, it should decrease to 0 and then raise again to 1.

I sometimes get a negative angle (-8.etc) and I don't understand why (the console.log is supposed to show that).

I made a pen for that : https://codepen.io/mourtazag/pen/QvjjLy

Thank you all !

Mourtaza.

Mourtazag
  • 57
  • 6

4 Answers4

0

You are having a case of unfortunate floating point math

0.94 - 0.01
0.9299999999999999

These errors can compound as you subtract 0.01 more and more times so in the end instead of doing 0.01 - 0.01 which would get 0, the code is instead doing something like

0.009999999999999247 - 0.01 

which is slightly negative. You could probably just bound the min and max with Math.max(0, yourValue) and Math.min(1, yourValue) if you need this to be bounded like that.

noveyak
  • 3,240
  • 1
  • 19
  • 19
0

It's a floating point issue

var a = 0;
for (var i = 0; i < 100; ++i) {
  a += 0.01;
} 
console.log(a);
for (var i = 0; i < 101; ++i) {
  a -= 0.01;
} 
console.log(a);
a += 0.01;
console.log(a);

prints

1.0000000000000007
-0.010000000000000087
-8.673617379884035e-17

consider using integers OR a clock and compute the angle from those

gman
  • 100,619
  • 31
  • 269
  • 393
0

You don't receive -8, you receive -8.XXXXXXe-17. Note the e-17 at the end. That's scientific notation, so your number is actually -0.00000000000000008XXXXX. You can see the float number without scientific notation using angle.toFixed(20). The number is the amount of decimals that you want to show.

Why you get such small numbers if you are only increasing and decreasing by 0.01? may you ask. This happens because floating points are not exact as decimals are impossible to be "saved to the end", not like integers. Think of 1/3 having an infinite numbers of decimals, for example. Here you can read more about JavaScript and float precision: Dealing with float precision in Javascript

The other problem is having a negative angle (such small, but still negative although you actually check for negative angles). This happens because you check for negative angles BEFORE increasing/decreasing it, so if you have your multiplier as negative and your angle as, let's say, 0.0001whatever (because of the float precision problem), it is still above zero, so you multiplier is still going to be negative, but is actually bigger than your angle, so when the multiplier is applied, the angle will be negative.

I can think of this way to solve this problem:

let angle = 0
let multiplier = .01

function raf() {

  angle = angle+multiplier;

  if(angle > 1 || angle < 0) {
    multiplier = -multiplier 
    angle = angle<0?0:1;
  }
  console.log(angle.toFixed(20))

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

With this, you check if your angle is inside the limits AFTER working on it, so you will overcome the precision problem: https://codepen.io/anon/pen/eWppGy

Another way can be to use integers (because they don't have the precision problem, as I stated above) and divide the angle by 100 when going to work with it, like this:

let angle = 0
let multiplier = 1

function raf() {

  angle = angle+multiplier;

  if(angle >= 100 || angle <= 0) {
    multiplier = -multiplier;
    angle = angle<=0?0:100;
  }
  console.log(angle/100)

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

Pen: https://codepen.io/anon/pen/XRmmVq

Community
  • 1
  • 1
Jorge Fuentes González
  • 11,568
  • 4
  • 44
  • 64
0

Off topic: A different approach to the angle. As a function of passed time rather than incremental change.

let start = Date.now();
let speed = 1 / 10000;  //1 in every 10 seconds

function raf() {
 let v = (Date.now() - start) * speed;
 let angle = v&1? 1-v%1: v%1;
  
 document.body.textContent = "Angle: " + angle.toFixed(3);
 requestAnimationFrame(raf);
}

//or with some sugar:
speed = 180/10000;
function raf() {
    let minAngle = -90, maxAngle = 90, delta = maxAngle - minAngle;

 let v = (Date.now() - start) * speed;
 let angle = (v/delta)&1? maxAngle-v%delta: minAngle + v%delta;
  
 document.body.textContent = "angle: " + angle.toFixed(3);
 requestAnimationFrame(raf);
}

raf();

or with some sugar; the whole computation when we're not talking about a range from 0 to 1

let start = Date.now();
let speed = 180 / 10000;
let minAngle = -30, maxAngle = 45;

function raf() {
 let delta = maxAngle - minAngle;
 let v = (Date.now() - start) * speed;
 let backwards = (v/delta)&1;
 let angle = backwards? maxAngle - v%delta: minAngle + v%delta;

 document.body.textContent = "angle: "+ angle.toFixed(3) +", direction: " + (backwards? "backward": "forward");

 requestAnimationFrame(raf);
}

raf();
Thomas
  • 11,958
  • 1
  • 14
  • 23