Some bug (may be) in Array.indexOf

Posted on
  • Hi @Gordon,

    While trying to do some statistics, I found that situation on a v2.00 upgrade Pico:

    >var tab1mAlpha = new Array( 0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 0.95, 0.99, 0.999);
    =[ 0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 0.95, 0.99, 0.999 ]
    >console.log(tab1mAlpha.indexOf(v))
    -1
    =undefined
    >v
    =0.95
    >typeof v
    ="number"
    

    However, using the test box in the mozilla page, I got

    var tab1mAlpha= [0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 0.95, 0.99, 0.999];
    var v=1-0.05;
    console.log(tab1mAlpha.indexOf(v));
    console.log(typeof v);
    

    which once run showed:

    > 8
    > "number"
    

    So, is this a bug or I am not understanding something?

    P.S.: Should I report this type of problem on the forum or in github?

  • Which board are you running this on?

    It looks like this is kind of a bug and kind of not a bug. What you're hitting is floating point inaccuracy.

    The issue boils down to whether this is true or false: 1-0.05 == 0.95

    You can't represent 0.05 or 0.95 exactly in a floating point number, so the nearest number is reported. So what you're doing is basically comparing 0.95 and 0.95000000001 (which just gets rounded in the console).

    In fact to show you the actual binary numbers behind o.95:

    >console.log(new Uint8Array(new Float64Array([0.95, 1-0.05]).buffer))
    new Uint8Array([
    103, 102, 102, 102, 102, 102, 238, 63, 
    102, 102, 102, 102, 102, 102, 238, 63])
    

    So there's one bit different in Espruino. I just checked with Node.js and it looks like the parsing of numbers is actually different, since Node.js gets 102, 102, 102, 102, 102, 102, 238, 63 for 0.95 as well but Espruino gets 103,...

    However it's really bad practice to do what you're doing anyway. Even if I fix this issue there will be cases where it fails. For instance the classic [0.3].indexOf(0.1+0.1+0.1) that used to trip up Windows Calculator still fails in Node.js (ironically it actually works in Espruino!).

    To work around this you could explicitly check if the number was close or not: tab1mAlpha.findIndex(a=>Math.abs(a-v)<0.­000001)

    I think this kind of thing is always better to check on the forum with first - I have now filed a bug but in many cases they can be things that have been asked before - very often on GitHub I just keep getting similar issues filed time after time.

  • Just to add that another option is just to compare strings...

    t = tab1mAlpha.map(x=>x.toString())
    t.indexOf(v.toString())
    
  • Thanks,
    Yes, I had forgotten about decimals beeing rounded somewhere when transformed into binaries.

    So I tested the second solution and it works.

    I totally agree with the no bug approach. The trick is that it's a silent killer. As well as dynamic typing is indeed.

    However, the value of v is continuous (risk level in probability) so the exact comparison done by Array.indexOf was not my need.

    I finally settled on Array.findIndex as I can get an index through a comparison function even if the value is really not in the array but rather in between values in the array. My array is sorted in increase order.
    And this came from your first answer. tab1mAlpha.findIndex(a=>Math.abs(a-v)<0.­000001)

    This page provides the formulas and statistics tables from which I extracted the 2 arrays below...
    That gave me:

    // approximately computes the xhi2 of 2 degrees of liberty value for alpha risk
      function xhi2_2(alphaRisk) { 
            var idx = [0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 0.95, 0.99, 0.999].findIndex(elt => elt>=(1-alphaRisk));
        return [0.10, 0.21, 0.45, 0.71, 1.39, 2.41, 3.22, 4.61, 5.99, 9.21, 13.82][idx];
      }
     
    

    Thank's again for your reactivity and commitment to understand our questions!

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Some bug (may be) in Array.indexOf

Posted by Avatar for asez73 @asez73

Actions