帧同步,浮点计算不一致问题研究

最近在研究帧同步的浮点计算问题,搞了好几天定点数。

测试了一下跨平台浮点问题的重灾区 sin cos sqrt。

测试了4个平台 原生 , firefox, chrome ,safari;

js的math库在4个平台上结果都不一样。

本来是测试一下定点数库的精度,偶然发现 wasm 下的 sin cos sqrt 在各个平台上却是一致的。

现在我在想,是不是可以直接用 wasm 的数学库来解决浮点数不一致问题。

有没有懂道的大佬。

测试结果 :

Native:
natvie

Chrome:
chrome

Firefox:
fireforx

Safari:
safari

测试代码:

        let count = parseInt(this.editBox.string);

        let sin_jsVal = 0;
        let sin_fpVal = new FP;
        let sin_asmVal = 0;

        let cos_jsVal = 0;
        let cos_fpVal = new FP;
        let cos_asmVal = 0;

        let sqrt_jsVal = 0;
        let sqrt_fpVal = new FP;
        let sqrt_asmVal = 0;

        let sct_jsVal = 0;
        let sct_fpVal = new FP;
        let sct_asmVal = 0;

        for (let i = 0; i < count; i++) {
            sin_jsVal += Math.sin(i);
            sin_fpVal = sin_fpVal.add(FPMath.sin(new FP(Raw._1 * BigInt(i))));
            sin_asmVal += WasmTest.module.sin(i);

            cos_jsVal += Math.cos(i);
            cos_fpVal = cos_fpVal.add(FPMath.cos(new FP(Raw._1 * BigInt(i))));
            cos_asmVal += WasmTest.module.cos(i);

            sqrt_jsVal += Math.sqrt(i);
            sqrt_fpVal = sqrt_fpVal.add(FPMath.sqrt(new FP(Raw._1 * BigInt(i))));
            sqrt_asmVal += WasmTest.module.sqrt(i);

            sct_jsVal += Math.sin(i) * Math.cos(i) * Math.sqrt(i);
            sct_fpVal = sct_fpVal.add(
                FPMath.sin(new FP(Raw._1 * BigInt(i)))
                    .mul(FPMath.cos(new FP(Raw._1 * BigInt(i))))
                    .mul(FPMath.sqrt(new FP(Raw._1 * BigInt(i)))));
            sct_asmVal += WasmTest.module.sin(i) * WasmTest.module.cos(i) * WasmTest.module.sqrt(i);
        }

        this.labelTest1.string =
            `
${_('sin(step)')}:
${_('js')}:\t${sin_jsVal}
${_('wasm')}:\t${sin_asmVal}
${_('fp')}:\t${sin_fpVal}
${_('fpRaw')}:${sin_fpVal.rawValue}

${_('cos(step)')}:
${_('js')}:\t${cos_jsVal}
${_('wasm')}:\t${cos_asmVal}
${_('fp')}:\t${cos_fpVal}
${_('fpRaw')}:${cos_fpVal.rawValue}

${_('sqrt(step)')}:
${_('js')}:\t${sqrt_jsVal}
${_('wasm')}:\t${sqrt_asmVal}
${_('fp')}:\t${sqrt_fpVal}
${_('fpRaw')}:${sqrt_fpVal.rawValue}

${_('sin(step)*cos(step)*sqrt(step)')}:
${_('js')}:\t${sct_jsVal}
${_('wasm')}:\t${sct_asmVal}
${_('fp')}:\t${sct_fpVal}
${_('fpRaw')}:${sct_fpVal.rawValue}
`
1赞

建议直接用定点数库,完全自己可控。
就算是wasm的sin/cos这些,应该也会受到cpu本身的浮点计算影响。你可以试试pc和手机浏览器,跑wasm的函数看看一致不一致。建议不要依靠这个。

我就是测试了 不同平台 苹果 安卓 电脑 ,wasm的sin cos sqrt 结果都是一致的。其他的函数还没测,不知道,如果可以直接用wasm,那就好了,定点数库 性能太拉了,而且写法很别扭。

三角函数读表。
四则运算和sqrt,你会发现v8本来在各个平台上就是一致的 :grin:

我刚刚去翻了一下wasm的文档。确定了几点:
1.wasm规范上,浮点数的运算是确定的,也就是一般的浮点数运算在各个平台上是一致的。但是牵涉到NaN的处理上是不确定的,他的NaN在比特位上是不确定的。
2.sin/cos 这些三角函数,不包含在wasm规范范围内,而是由外部的数学库实现的。换句话说,这些函数的实现依赖于你的数学库,不一定一致。

上面楼上说的情况也和这个一致,如果浮点数不牵涉到取NaN的字节表述。那可以看成是确定的。但是如果你的数学库本身不支持三角函数的一致性,就不能保证三角函数的结果是否一致。

建议:四则运算是一致的,三角函数最好自己弄个表比较好,避免数学库的问题。

可以参考
https://github.com/WebAssembly/design/blob/main/Rationale.md
的 Limited Local Nondeterminism 一节
三角函数这块,参考
https://github.com/WebAssembly/design/blob/main/FAQ.md
的 Why is there no fast-math mode with relaxed floating point semantics? 一节。

13赞

真是个让人安心的队友

2赞

有这种难得的钻研分享精神,建议年终多发一薪

而你我的朋友,你是真正的英雄

1709195345721_C5D9CE60-8F21-4dd3-BEF6-9629B0CF7D80
所以, V8和JSC怎么同步…

乘以10的倍数,结果向下取整,再除回来。export const toFixed = (num:number,digit = 3)=>{
const scale = 10 ** digit;
return Math.floor(num * scale) / scale;
}

1赞

以前我也这么搞,后来有人出现这种情况
Screenshot_20240301_105923

怎么出现的啊,,

除的时候也应该向下取整

0.56 和 0 那能一样么

神奇的js

脑残了,还是用toFixed约定尾数吧 :joy:

别用乘除法做帧同步了,不用定点数就能实现不香吗? :sweat_smile:

浮点数精度问题 toFixed一样的吧 要么切割浮点数 把保留位数后面的小数舍弃掉 要么使用toPrecision

1赞


每次git提交都有一堆小数问题