1

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 : %d\n", 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

Edit Feb/2024: There is now CPAN module Math::JS (at https://metacpan.org/pod/Math::JS) by Sisyphus.

4
  • 1
    javascript has no concept of signed 32 bit in general ... your code, in javascript, just is a Number + Number ... if you want 32 bit signed ... ((a<<b) + (a << b)) | 0 that will force the answer to be the 32 bit signed you expect Commented Dec 1, 2023 at 10:11
  • I hear you! That's why that code also had what you suggested to force it to 32bit integer. My question is how to emulate that behaviour in C. Or why there is that discrepancy with C/Perl? Commented Dec 1, 2023 at 10:38
  • 1
    They are just different languages. :-) And 2147483648 is not a signed 32-bit value, it is one unit too big. So you could used unsigned values, or 64-bit signed ints. Commented Dec 1, 2023 at 11:25
  • 1
    In C your code invokes undefined behavior because of signed int overflow. (It was some 20 years since I coded in Perl but iirc it's the same thing there.) Meaning there is no guaranteed output or program outcome. Commented Dec 1, 2023 at 11:31

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

may vary between ECMAScript and C IMO that's somewhat of an understatement. Given double a;, the cast to 32-bit signed integer in (int32_t) a will invoke undefined behavior in C if the value of a can not be represented as an int32_t. (I know you know that - but I suspect most readers likely won't appreciate the risk of using a simple cast for the conversion.)
There are some more complexities, plus the caveats mentioned. So, for the time being, it is safer and easier to shell-out the JS code to node.js. Thanks again
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 : %f\n", res);
}

1 Comment

In JavaScript (ECMAScript), the right operand of a left shift is converted to uint32_t, not int32_t.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.