• Thr 2019.07.25

    This is a rather strange anomally, and not what was originally tackled.

    Foundation:
    Was in the process of converting this C++ script into Javascript:

    void FClass::setVal(long val)
    {
      uint64_t frf = ((uint64_t)val << 19) / 32000000;
    
      writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
      writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
      writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
    }
    

    C/C++ uses a long int 64 bits, so shifting 19 places will work, and it appears that the uint8_t typecast is being used to strip leading and trailing bytes.

    Kept getting incorrect result. Had a hunch that left shifing 19 places might be an issue

    https://stackoverflow.com/questions/2063­4241/javascript-bug-left-bit-shift-retur­ns-wrong-output

    Down a rabbit hole, . . . but the wrong one!

    After manually creating binary strings

    0001101101000100111001010110000000000000­000000000000 / 0001111010000100100000000000

    determined that the bits were getting sent to no mans land during left shift, as expected.

    Verified how Javascript vars work:

    https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/Number/MAX_SAFE_INTEGER

    "The MAX_SAFE_INTEGER constant has a value of 9007199254740991 (9,007,199,254,740,991 or ~9 quadrillion). The reasoning behind that number is that JavaScript uses double-precision floating-point format numbers as specified in IEEE 754 and can only safely represent numbers between -(253 - 1) and 253 - 1."

    https://stackoverflow.com/questions/3071­79/what-is-javascripts-highest-integer-v­alue-that-a-number-can-go-to-without-los­in

    "They are 64-bit floating point values, the largest exact integral value is 253-1, or 9007199254740991 . In ES6, this is defined as Number.MAX_SAFE_INTEGER. Note that the bitwise operators and shift operators operate on 32-bit ints, so in that case, the max safe integer is 231-1, or 2147483647."

    So the data should fit inside that value, as we are one third of the max value. Realized that as left shifting is nothing more than doubling, tried the same using Math.pow()


    Find that rabbit hole.

    Process:
    Wrote many string representation formatting functions to show the decimal values shown using the WebIDE debugger as both formatted hex and formatted binary to determine if it was in fact a shift issue.

    It didn't appear to be, . . . until . . .

    Spent two days attempting to understand why a previously working/tested format function all of a sudden started giving incorrect output. In a final act of desperation, started whacking out everything that wasn't needed from the 700+ lines of code. Still had the incorrect output.

    Decided to make Html scripting pages to test. They tested fine. So where was the issue?



    Found the rabbit hole.

    Observation:
    I made a copy of the original data in a new var assignment. An Viola! Found what was causing what appeared to be a bit shift issue. Still not quite sure, as the debugger doesn't reveal anything new on the right-hand editor side of the WebIDE. But it appears to be the assignment of an existing var!

    The offending lines. They appear to be valid to me. See comment at top of source file for commented out lines. Files contain identical source, just more comments in 'incorrect' version.

    var midsav = newDiv;
    var lsbsav = newDiv;
       ... or
    var midand = midsav & 0x0000FF00;
    var lsband = lsbsav & 0x000000FF;
    

    Google's closure compiler shows zero errors

    https://closure-compiler.appspot.com/

     	Compilation was a success!
    Original Size:	569 bytes gzipped (1.13KB uncompressed)
    Compiled Size:	255 bytes gzipped (470 bytes uncompressed)
    
    Correct output --  0xE4C000
    >L45 msband=E40000
    L46 midand=C000
    L47 lsband=0
    >
    

    And with the two lines commented in:

    *** Incorrect output ***
    >L45 msband=E40000
    L46 midand=BF00
    L47 lsband=FF
    >
    



    Hoisting doesn't appear to be an issue, nor does redefining:

    "If you re-declare a JavaScript variable, it will not lose its value."

    https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Statements/var­



    Works in Chrome, either .htm files demonstrate this.

    >process.memory()
    ={ free: 7016, usage: 132, total: 7148, history: 140,
      gc: 0, gctime: 6.53553009033, "stackEndAddress": 536991644, flash_start: 134217728, "flash_binary_end": 460040,
      "flash_code_start": 134283264, flash_length: 524288 }
    >process.env
    ={
      VERSION: "2v04",
      GIT_COMMIT: "3956264e",
      BOARD: "ESPRUINOWIFI",
      FLASH: 524288, RAM: 131072,
      SERIAL: "3d003100-0b513532-39333638",
      CONSOLE: "USB",
      MODULES: "Flash,Storage,hea" ... ",neopixel,Wifi,AT",
      EXPTR: 536871212 }
    > 
    

    Spending as much time on this as I have, I would really like to understand where in the source this culprit exists. I'll try a few attempts at renaming the var declaration and assignment, to see if that may be part of the issue.

    The two lines commented out are indicated in each source file at page top.

  • Ok, so summarised the correct.js and incorrect.js output the same in browser. Tested in node, they do output the same:

    > node .\correct.js
    L145 msband=E40000
    L146 midand=C000
    L147 lsband=0
    > node .\incorrect.js
    L145 msband=E40000
    L146 midand=C000
    L147 lsband=0
    

    , but different in Espruino, right?

    My guess is rounding is somewhat messed up, the interesting part:

    >var val = 915E6;
    =915000000
    >var newVal = val * Math.pow(2, 19);
    =479723519999999.9375
    >var newDiv = newVal / 32000000
    =14991360
    // but doesn't equal to the literal value:
    >newDiv==(14991360)
    =false
    // it's smaller:
    >newDiv<(14991360)
    =true
    >newDiv>(14991360)
    =false
    

    So it's not exactly 14991360, but a bit smaller. In your correct version you set it to the exact literal value, that's why it behaves differently.

    // Espruino:
    >newDiv & 0xff00
    =48896
    
    // with literal value
    >14991360 & 0xff00
    =49152
    

    If you round it, you get what you expect:

    >Math.round(newDiv) == 14991360
    =true
    >Math.round(newDiv) & 0xff00
    =49152
    
    // in hex:
    >(newDiv & 0xff00).toString(16)
    ="bf00"
    >(Math.round(newDiv) & 0xff00).toString(16)
    ="c000"
    

    That doesn't mean I have an explanation, probably a different behaviour with bitwise operations on fractions in Espruino.
    Maybe a bug? Because after explicit rounding it works the same as in node / browser.

About

Avatar for AkosLukacs @AkosLukacs started