将 Javascript 的 32 位有符号整数算术模拟为 C(或 Perl) - 一些差异

问题描述 投票:0回答:2

我正在尝试将简单的 JS 代码转换为 C 和/或 Perl,但我发现对整数进行算术运算 (

+ - * / << >>
) 时行为存在差异,并且结果溢出。我需要精确地模拟JS,甚至溢出! JS 变量不是显式的 BigInt,而只是 JS var。

JS(通过node.js或Firefox的开发者工具控制台):

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

我的说法是否存在差异,或者我做错了什么?

如何做到这一点?

正如我所说,我需要在 C/Perl 中模拟 JS 的确切行为,所以我不想修复 JS 代码,而是调整 C/Perl 代码以执行与 JS 相同的操作。

另请参阅相关讨论:https://perlmonks.org/?node_id=11155911

2024 年 2 月编辑:现在有 Sisyphus 的 CPAN 模块 Math::JS(位于 https://metacpan.org/pod/Math::JS)。

javascript c perl overflow integer-arithmetic
2个回答
4
投票

JavaScript 是 ECMAScript 的实现,ECMAScript 指定执行左移操作,就像在执行操作之前将左操作数转换为 32 位二进制补码整数,将右操作数转换为 32 位无符号整数整数。它还指定使用 IEEE-754 二进制 64(“双精度”)形式的数字执行加法。

假设您的 C 实现对

double
使用 binary64(这很常见),并且
int32_t
uint32_t
可用(在
<stdint.h>
标头中),则 JavaScript
(a<<b) + (a<<b)
很大程度上相当于:

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

我说“很大程度上等效”是因为 ECMAScript 和 C 之间涉及类型之间转换或处理无穷大、NaN 和其他异常情况的操作细节可能有所不同。

ECMAScript 规范对于操作的语义是明确的。如果您要将 JavaScript 转换为另一种语言,您应该获取一份 ECMAScript 语言规范 并使用它。


1
投票

Javascript“数字”本质上是

double
,在进行位算术时,它们被截断为
int32
,然后提升回双精度。所以你的 C 代码应该是这样的

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);
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.