Emscripten 中的“大”整数

Posted

技术标签:

【中文标题】Emscripten 中的“大”整数【英文标题】:"Big" integers in Emscripten 【发布时间】:2017-11-16 06:27:05 【问题描述】:

我正在使用 Emscripten 并编译一些简单的程序来测试它的性能。这是我快速整理的fib(n) 算法。实现并不重要,但如果您需要,这里是源代码:

#include <stdlib.h>
#include <stdio.h>

#define KEY_TYPE unsigned int
#define VALUE_TYPE unsigned long long

// Simple linked list implementation
typedef struct list
    KEY_TYPE key;
    VALUE_TYPE value;
    struct list* next;
 list;

void free_list(list** headPtr)
    list* head = *headPtr;
    while(head)
        list* next = head->next;
        free(head);
        head = next;
    


VALUE_TYPE lookup_list(list** headPtr, KEY_TYPE key)
    for(list* head = *headPtr; head; head = head->next)
        if(head->key == key)
            return head->value;
        
    
    return 0;


void add_list(list** headPtr, list* block)
    block->next = *headPtr;
    *headPtr = block;


VALUE_TYPE fib_recur(list** lookup, KEY_TYPE n)
    VALUE_TYPE value;
    // base cases
    if(n < 2)   return n;

    // look for cached answer
    value = lookup_list(lookup, n);
    if(value > 0)   return value;

    // calculate answer
    value = fib_recur(lookup, n - 1) + fib_recur(lookup, n - 2);

    list* head = calloc(sizeof(list), 1);
    head->key = n;
    head->value = value;
    add_list(lookup, head);

    return value;



VALUE_TYPE fib(n)
    list* listPtr = NULL;
    VALUE_TYPE num = fib_recur(&listPtr, n);
    free_list(&listPtr);
    return num;


int main(int argc, char** argv)
    if(argc != 2)   return 1;

    KEY_TYPE key;
    sscanf(argv[1], "%d", &key); 
    VALUE_TYPE value = fib(key);
    printf("fib(%d) = %lld\n", key, value);
    return 0;

C 中的实际实现是正确的(通过使用 clang 编译测试。)在 Node.js 中,它适用于小整数,但是当我尝试 47 时,fib(47) 返回 -1323752223,这是不正确的。

var Module = require("./fib.js");
var fib = Module.cwrap("fib", "number", ["number"]);

for(var i = 1; i <= 90; i++)
    console.log(i, fib(i));

45 1134903170
46 1836311903
47 -1323752223  <-- overflow?
48 512559680    <-- all numbers are incorrect after this point
49 -811192543

这是为什么呢?我用来编译C代码的命令如下:

emcc fib.c -O1 -o fib.js -s EXPORTED_FUNCTIONS="['_fib']"

【问题讨论】:

emscripten-1.37.1 和 emscripten-1.35.0 看起来没有溢出。 【参考方案1】:

在 JS 中的 AFAIK,您将所有数字表示为 doubles,并且使用它们模拟 Asm.js 子集中的整数。但是 64 位 int 不能用 double 表示,所以据我记得,在 Emscripten 调用约定中,long long 的最高 32 位是使用一些全局临时变量返回的。

使用调试信息编译您的程序(-g3 编译器选项)(您可能必须尝试不同的优化级别:在-O0 上生成的代码可能太吵了,但在-O3 上可能不容易了解)并检查您的 main 函数,了解您的 _fib 是如何被调用的以及返回值是如何获取的。但我担心,这种调用约定可能会在未来的 Emscripten 版本中发生变化。同时,您可以尝试阅读 Emscripten 的 val.hbind.h 上的文档,但我自己从未使用过。

【讨论】:

以上是关于Emscripten 中的“大”整数的主要内容,如果未能解决你的问题,请参考以下文章

Emscripten 中的 C++11 支持

使用 Clang 编译的可执行文件中的 emscripten

您如何在 emscripten 中迭代 Box2D 中的实体列表?

如何使用 Emscripten 编译的程序中的 wasm-bindgen?

Web Worker 中的 Emscripten WASM:“模块不是对象或函数”

Emscripten 中的“Module”变量机制,同时将 Pthread 编译为 worker