skip to Main Content

I am trying to translate simple JS code to C and/or Perl but I found a discrepancy in behaviour when doing arithmetic operations (+ - * / << >>) on integers and the result overflows. I need to simulate JS exactly, even the overflows! JS variables are not explicitly BigInt but just JS var.

JS (via node.js or Firefox’s developer tools’ console):

function calc(a, b){
 return (a<<b) + (a<<b);
}
var x = calc(1, 30);
result:
2147483648

C:

#include <stdio.h>
#include <stdint.h>
int main(void){
  int32_t lop = 1<<30; //1073741824;
  int32_t rop = 1<<30; //1073741824;
  int32_t res = lop + rop;
  printf("1<<30 + 1<<30 : %dn", res);
}
result:
1<<30 + 1<<30 : -2147483648

Perl:

sub to32bit { unpack("l", pack("L", $_[0])) } # Corion @ PerlMonks
print to32bit(to32bit(1<<30)+to32bit(1<<30));
result:
-2147483648

Am I right that there is a discrepancy or am I doing things wrong?

How to get this right?

As I said I need to emulate JS’s exact behaviour in C/Perl so I don’t want to fix JS code but adjust C/Perl code to do the same as JS.

See also related discussion at https://perlmonks.org/?node_id=11155911

2

Answers


  1. Javascript "Numbers" are essentially doubles, when doing bit arithmetics they are truncated to int32‘s and then promoted back to doubles. So your C code should be something like

    double js_shl(double a, double b) {
        return (double) ((int32_t)a << (int32_t)b);
    }
    
    double calc(double a, double b) {
        return js_shl(a, b) + js_shl(a, b);
    }
    
    int main(void){
      double res = calc(1, 30);
      printf("result : %fn", res);
    }
    
    Login or Signup to reply.
  2. JavaScript is an implementation of ECMAScript, and ECMAScript specifies that a left-shift operation is performed as if the left operand were converted to a 32-bit two’s complement integer before the operation were performed and the right operand were converted to 32-bit unsigned integer. It also specifies that addition is performed using numbers in the IEEE-754 binary64 (“double precision”) form.

    Assuming your C implementation uses binary64 for double, which is very common, and that int32_t and uint32_t are available (in the <stdint.h> header), then JavaScript (a<<b) + (a<<b) is largely equivalent to:

    (double) ((int32_t) a << (uint32_t) b) + (double) ((int32_t) a << (uint32_t) b)
    

    I say “largely equivalent” because details of operations involving conversion between types or handling of infinities, NaNs, and other exceptional cases may vary between ECMAScript and C.

    The ECMAScript specification is explicit about the semantics of operations. If you are converting JavaScript to another language, you should get a copy of the ECMAScript Language Specification and use it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search