英文:
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
答案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)
}
没有定义 a
、b
或 c
。而表达式 (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?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论