【问题标题】:Issue while calculating float value using NodeJS N-API使用 NodeJS N-API 计算浮点值时出现问题
【发布时间】:2018-11-20 22:43:55
【问题描述】:

大家好,

来自网络开发世界。我目前正在尝试执行一些 C 代码,将 RGB 值转换为 NodeJS 通过 N-API 可以使用的 XYZ 值。我遇到的问题是关于浮点计算。以下是我的问题的解释:

基于下面这段 C 代码,这段代码试图将 RGB 值转换为 XYZ 值。

char * colorType = getStringValue(env, funcParams[2], SPACELen);
// m is either the value srgb | adobeRgb
Matrix m = getEnumFromStr(colorType);
Rgb * rgb = getRGBFromJSObj(env, funcParams[0]);
xyz = generateXyzFromRgb(rgb, m);

我正在使用这个 JS sn-p 来调用我的库

const rgb = {
  r: 255,
  g: 255,
  b: 255
};

const xyz = lib.getXyzFromRgb(rgb, "srgb", 10000);
expect(xyz).to.be.deep.equal({
  x: 0.9504,
  y: 1,
  z: 1.0888
});

如果一切正常,输出应该如下所示

{
 x: 0.9504,
 y: 1,
 z: 1.0888 
}

但是我的输出是这个

{
 x: 0.9502,
 y: 0.9997,
 z: 1.0886 
}

如您所见,输出完全错误。 然而,这个错误的输出只发生在我的本地机器 (OSX) 上,并且仅在我尝试使用 JS sn-p 进行转换时发生。

确实,当我尝试使用下面的这段代码直接通过 Xcode 运行转换时,输出是正确的

// RGB and & m variable is outputing the same value as the conversion done by the C code above
xyz = generateXyzFromRgb(rgb, m);

此外,当我尝试通过运行 OSX 的 travis 调用 JS 代码时,以及通过 Docker 在 Ubuntu 上调用时,JS 代码也会输出正确的值

它是否与硬件或我编译库的方式相关?

提前谢谢你。

【问题讨论】:

  • C代码中有哪些数值数据类型?
  • 一般情况下,在数字是二进制浮点值的任何编程语言中,执行严格的相等测试都很棘手。
  • @Pointy 数字数据类型是浮点数。我应该把它改成双倍吗?确实,我查看了看起来很棘手的 IEE754……但是我发现错误的输出仅发生在我的机器上并且仅在使用 JS sn-p 而不是在 travis osx 或 ubuntu 中发生时,这很奇怪
  • C float 类型(通常)只有 32 位; JavaScript 数字是 64 位。
  • @Pointy 嗯,是的,在使用浮点值之后,我将浮点数转换为双精度值,以便 N-API 使用该值。您是否认为在此转换过程中可能会丢失精度?我想我应该立即尝试将其转换为 double 并看看效果如何

标签: javascript c node.js node.js-addon node.js-napi


【解决方案1】:

在 javascript v8 引擎中。实际上只存在smi和double。 readFloatBE:当 float 传递到 v8 时,float 将被强制转换为 double。 当你出去并四舍五入为浮点数时,它将提升为双精度。

首先,如果你真的想得到像C这样的值,你需要手动指定四舍五入的数字,并用四舍五入的Number.prototype.toPrecision函数重新实例化Number对象:

供您参考:

var v = 5.2
var buffer = new Buffer(5)
buffer.writeFloatBE(v)
var g = buffer.readFloatBE()

console.log(v)
console.log(g)
console.log(v==g)
console.log(Number(g.toPrecision(5)))

【讨论】:

  • 确实。我可以返回一个缓冲区而不是一个浮点值,这可能有助于提高精度
【解决方案2】:

事实上,Pointyuser10683038 发布的 JavaScript 数字是 64 位的。

当我编写此功能时,使用的数字类型是浮点数(乍一看并没有考虑制作 Node 模块)。后来我决定使用下面的小工具方法从 float 到 double 进行转换

double v = *(double *) arg;
status = napi_create_double(env, v, &value);

奇怪地似乎我在进行此转换时可能会随机丢失小数精度,而它不应该(32 位到 64 位),或者我可能遗漏了一些东西。

稍后我重构了相同的代码,但这次通过 使用 double 代替,它可以正常工作。非常感谢大家。

注意:当我在 NodeJS 绑定代码库中使用上述方法的这个 util 代码时,我很惊讶我之前没有遇到小数精度错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 1970-01-01
    • 2021-09-06
    • 2021-09-20
    • 1970-01-01
    • 2021-08-09
    • 2020-10-20
    相关资源
    最近更新 更多