JavaScript 和 Rust 函数之间返回值偶尔会有差异。

huangapple go评论78阅读模式
英文:

Occasional differences in return values between JavaScript and Rust functions

问题

以下是翻译好的内容:

以下的JavaScript函数一直按预期工作:

function getvalue(a, b, c) {
    Math.floor((a / b) % c)
}

当我在Rust中编写这个函数,并通过wasm_bindgen在JS中使用它时,偶尔会出现看似随机的情况,结果与上面函数返回的值不同:

pub fn get_value(a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

一些例子:

a = 33339077
b = 53.32715671989507
c = 3.5454545454545454

// JS -> 3, Rust -> 2

a = 33340860
b = 53.32715671989507
c = 3.5454545454545454

// JS -> 0, Rust -> 1

我在这里漏掉了什么,以及如何使Rust函数返回与JS函数相同的值?

英文:

The following JavaScript function consistently works as expected:

function getvalue(a, b, c) {
    Math.floor((a / b) % c)
}

When I write this in Rust and use it within JS via wasm_bindgen, every once in a while and seemingly randomly I get a result which is different than the value returned from the function above:

pub fn get_value(a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

Some examples:

a = 33339077
b = 53.32715671989507
c = 3.5454545454545454

// JS -> 3, Rust -> 2

a = 33340860
b = 53.32715671989507
c = 3.5454545454545454

// JS -> 0, Rust -> 1

What am I missing here, and what can be done to make the Rust function return the same value as the JS one?

答案1

得分: 4

JavaScript 使用双精度浮点数,但你的 Rust 函数使用单精度。如果你切换到 f64,你将得到相同的结果:

pub fn get_value32 (a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

pub fn get_value64 (a: f64, b: f64, c: f64) -> i32 {
    ((a / b) % c).floor() as i32
}

fn main() {
    println!("f32: {}", get_value32(33339077., 53.32715671989507, 3.5454545454545454));
    println!("f64: {}", get_value64(33339077., 53.32715671989507, 3.5454545454545454));
}

输出:

f32: 2
f64: 3
英文:

JavaScript uses double-precision floating-point numbers, but your Rust function uses single-precision. If you switch to f64 you will get the same result:

pub fn get_value32 (a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

pub fn get_value64 (a: f64, b: f64, c: f64) -> i32 {
    ((a / b) % c).floor() as i32
}

fn main() {
    println!("f32: {}", get_value32 (33339077., 53.32715671989507, 3.5454545454545454));
    println!("f64: {}", get_value64 (33339077., 53.32715671989507, 3.5454545454545454));
}

gives:

f32: 2
f64: 3

Playground

答案2

得分: 0

f32 类型的最大安全整数是 16777215。超过此值的整数无法精确存储。请参考以下 Rust 代码:

fn main() {
    let a: f32 = 33339077.0f32;
    println!("{a:?}");
    // 输出:33339076.0
}

可以表示为 33339077 的最接近值是 33337076,因此您的 a 实际上是 33339076,而不是 33339077。

您可能会注意到,(33339076/b)%c 等于 2.98713,而 (33339077/b)%c==3.0068869(即使使用更精确的算术也是如此)。向下取整时,一个结果是 2,另一个结果是 3。

要修复您的问题,请在代码中始终使用 f64,而不是 f32。请注意,将 a 存储为 f32 的任何时候都会引入不精确性,在调用函数之前将其转换为 f64 将会太晚,数据已经丢失。

英文:

The maximum safe integer for a f32 is 16777215. Integers above this value can not be stored exactly. See this rust code:

fn main() {
    let a:f32 = 33339077.0f32;
    println!("{a:?}");
    // Output: 33339076.0
}

The nearest value to 33339077 that can be represented is 33337076 so your a will actually be 33339076, not 33339077.

You may notice that (33339076/b)%c is 2.98713 while (33339077/b)%c==3.0068869 (even when using more accurate arithmetic). When flooring one comes out to 2 and the other 3.

To fix your problem use f64 throughout your code instead of f32. Note that storing a as a f32 at any point will introduce the inaccuracy, converting to f64 just before calling your function will be too late, the data is already lost.

答案3

得分: -5

以下是翻译好的内容:

你的JS函数

function getvalue() {
    Math.floor((a / b) % c)
}

没有定义 abc。而表达式 (a/b) % c 返回未定义。

但这并不重要,因为 getvalue() 默认情况下返回的是未定义值。

你的Rust函数

pub fn get_value(a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

假设 f32 是单精度IEEE浮点数,i32 是32位整数。


嗯,它也不返回值。

但更重要的是,JavaScript 中的所有数值都是IEEE双精度浮点数。

请参阅Goldberg的论文,标题是“每位计算机科学家都应了解的浮点算术”。

https://pages.cs.wisc.edu/~david/courses/cs552/S12/handouts/goldberg-floating-point.pdf

https://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

还可以查看 每位程序员都应了解的浮点数算术,或者为什么我的数字不加起来?

英文:

Well, your JS function

function getvalue() {
    Math.floor((a / b) % c)
}

Doesn't define a, b, or c. And the expression (a/b) % c yields undefined.

But it doesn't matter, because getvalue() doesn't return a value — it returns, by default, undefined.

Your Rust function

pub fn get_value(a: f32, b: f32, c: f32) -> i32 {
    ((a / b) % c).floor() as i32
}

Assuming that f32 is a single precision IEEE float and i32 is a 32-bit int,
.
.
.
Well, it doesn't return a value either.

But more (and most?) importantly, all numeric values in JS are IEEE double precision floats.

See Goldberg's paper, "What Every Computer Scientist Should Know About Floating-Point Arithmetic"

https://pages.cs.wisc.edu/~david/courses/cs552/S12/handouts/goldberg-floating-point.pdf

https://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Also see What Every Programmer Should Know About Floating-Point Arithmetic,or, Why don’t my numbers add up?

huangapple
  • 本文由 发表于 2023年6月16日 14:41:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76487543.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定