2

I have a simple interval which subtracts 0.1 each time around. But the number sequence goes weird after 3 iterations... this is what i have:

function transition_opacity(div_id,opacity){
    opacity = 1; //temporary test
    var IntervId = setInterval(process_transition,30);

    function process_transition(){
        console.log(opacity); //check the value
        opacity = opacity -  0.1
        div_id.style.opacity = opacity;
    if(opacity < 0.0){
            rmv_div(div_id);
            clear();
        }
    }

    function clear(){
         clearInterval(IntervId);
    }
}

The console log shows this for the value of opacity:

1
0.9 
0.8 
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013 
0.30000000000000016
0.20000000000000015
0.10000000000000014
1.3877787807814457e-16 

Why is it doing this crazy number sequence =/ doesn't seem to make sense to me... it works fine up to 0.8

Sir
  • 8,135
  • 17
  • 83
  • 146

3 Answers3

1

Floating point math is not exact for some values that cannot be perfectly represented the way floating point stores its numbers. All you're seeing is a slight difference in the exact value.

This can usually be worked around by rounding your value to a certain number of decimal places. You can read as much as you want about floating point accuracy with a simple Google search on the topic. A classic work-around is to round your value to a certain number of decimal points.

But, your function actually still works fine because you are just looking for < 0 which will still give you the proper number of iterations even with the minor discrepancy in value.

You don't actually need this for your function to work properly, but if you wanted exactly values, you could round to one decimal place like this:

function transition_opacity(div_id,opacity){
    var opacity = 1; //temporary test
    var IntervId = setInterval(process_transition, 30);

    function process_transition(){
        console.log(opacity); //check the value
        opacity = Math.round((opacity -  0.1) * 100) / 100;
        div_id.style.opacity = opacity;
        if (opacity < 0) {
            rmv_div(div_id);
            clear();
        }
    }

    function clear(){
         clearInterval(IntervId);
    }
}

FYI, I also declared opacity to be a local variable.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I thought that but in my JSFiddle the check for `< 0` still seems to not work: http://jsfiddle.net/4kWCB/12/ – Sir Oct 16 '13 at 22:27
  • @Dave - works with my code here: http://jsfiddle.net/jfriend00/8Yc7k/. I'll look into why it isn't working with yours. – jfriend00 Oct 16 '13 at 22:31
  • @Dave - your doesn't work because div_id is a string, not a DOM element and thus the `process_transition()` function throws an exception before the opacity is checked and aborts each time. See in my jsFiddle how I turned div_id into a DOM element or here's your code with that problem fixed: http://jsfiddle.net/jfriend00/mM7wQ/. Please remember to check your browser error console because it will often show you what is going wrong in situations like this. – jfriend00 Oct 16 '13 at 22:34
  • Ah that would explain why it worked on my website but not in my fiddle xD Thanks for explaination! – Sir Oct 16 '13 at 22:37
0

That is because of the way floating numbers are represented. The computer will give you, as exact as it can get, the value 0.7. However, because it is not using base 10, the most exact value that it can get to close to 0.7 is going to be 0.7000000000000001. When you subtract another 0.1, the same thing happens, and so on down the list.

Travis J
  • 81,153
  • 41
  • 202
  • 273
0

Javascript floating point is known for this sort of problem; it's due to (and I quote the good book here), "[Javascript] having adopted the IEEE Standard for Binary Floating Point Arithmetic (IEEE 1754)".

The recommended solution is to work in integer values - for your scenario, it makes logical sense to multiply the opacity value by 100, do your calculations, then divide by 100 to convert the value back to a decimal value.

robyaw
  • 2,274
  • 2
  • 22
  • 29