代码中乘 1.0f/72 和直接除 72f 有什么区别呢?

2022-11-08 10:36:01 +08:00
 nnegier

看 Android SDK 的 TypedValue#applyDimension 有感。 贴代码:

    public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
3079 次点击
所在节点    程序员
21 条回复
weeei
2022-11-08 10:39:09 +08:00
没有区别,这作者写的字面量都不统一。
效果是一样的,个人代码风格而已
最后 return 0 ,而不是 return 0.0f 就写的很随意。
fds
2022-11-08 10:45:05 +08:00
谷歌一下呗。除法比乘法慢。代码中这里的可以确定结果的除法在编译时就算出来了,所以运行时就只用乘法,不会影响速度。
https://stackoverflow.com/a/4125074/502851
Maboroshii
2022-11-08 10:47:29 +08:00
可能更想表达 72 分之 1 ?
ysc3839
2022-11-08 10:53:53 +08:00
@fds 但编译器没有那么蠢吧?如果乘法更快的话为什么不优化成乘以倒数?
mxT52CRuqR6o5
2022-11-08 11:05:13 +08:00
@ysc3839 c 的话肯定能靠编译器优化好不需要写的这么拐弯抹角,java 我就不清楚了
dcsuibian
2022-11-08 11:05:17 +08:00
@ysc3839 算倒数,还得先除
maggch97
2022-11-08 11:05:45 +08:00
@ysc3839 因为这样优化会有精度损失
maggch97
2022-11-08 11:12:31 +08:00
@mxT52CRuqR6o5 GCC 要做这个优化也是需要开 -ffast-math 的,而且一般是不会开这个编译选项的
ysc3839
2022-11-08 11:15:32 +08:00
@mxT52CRuqR6o5 Java 应该也有优化的,之前在网上听说 Java 等带虚拟机的运行时,长时间运行性能是能超越 C++ 这种编译好的程序的,因为虚拟机能在运行时统计性能数据并优化代码。

@maggch97 这个解释是最合理的。
shijingshijing
2022-11-08 11:16:15 +08:00
@dcsuibian 错了,直接做除法比乘以倒数慢多了,如果我记得没错的话,除法是用无限逼近的方法实现的,而乘法有硬件直接支持,现在的 CPU 和 GPU ,内部都有 MAD 或者 FMAD 指令,能够在很短时间直接出结果。而求倒也有快速实现的方法。所以综合起来先求倒再做乘法比直接除要快很多。

https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation
leonshaw
2022-11-08 11:21:19 +08:00
@ysc3839 浮点数这两个不等价
star9029
2022-11-08 11:26:36 +08:00
clang -O1
float test(float x) { return x / 2; }
优化成 x * 0.5
这种优化在 native 语言都是基操,java 就不清楚了
maggch97
2022-11-08 11:43:45 +08:00
@star9029 /2 跟 /72 是两个问题,https://godbolt.org/ 在这上面玩一下看看吧
maggch97
2022-11-08 11:45:44 +08:00
编译器开到 -O1000 都不会把 /72 优化成 *1/72
star9029
2022-11-08 11:52:09 +08:00
@maggch97 感谢指正,我没留意到这个优化有区别
ho121
2022-11-08 11:54:24 +08:00
```
>>> 111 / 72
1.5416666666666667
>>> 111 * (1/72)
1.5416666666666665
>>>
```

编译器不可能默认做这种优化
icyalala
2022-11-08 14:15:32 +08:00
https://godbolt.org/z/jWxqEGT9q
一个是 fmul ,一个是 fdiv ,不是等同的,编译不能优化
xz410236056
2022-11-08 14:29:50 +08:00
@shijingshijing #10


icelake 也有一个 div
shijingshijing
2022-11-08 14:51:46 +08:00
@xz410236056 这个除法我记得是用 MicroCode 实现的,而 FMAD 是纯硬件电路。这篇文章里面讨论了这个问题:

https://stackoverflow.com/questions/40354978/why-does-c-code-for-testing-the-collatz-conjecture-run-faster-than-hand-writte/

里面有人给出了数据:
乘法:
Also mul rbx on the OP's Haswell CPU is 2 uops with 3c latency (and 1 per clock throughput). imul rcx, rbx, 3 is only 1 uop, with the same 3c latency. Two ADD instructions would be 2 uops with 2c latency.
除法:
e.g. on the OP's Haswell CPU: DIVSD is 1 uop, 10-20 cycles latency, one per 8-14c throughput. div r64 is 36 uops, 32-96c latency, and one per 21-74c throughput. Skylake has even faster FP division throughput (pipelined at one per 4c with not much better latency), but not much faster integer div.

新的 x86 和 x64 的指令集和架构设计有很多优化和变更,我也没有怎么关注这方面最新的动态,但基本上都默认了除法比乘法慢很多。
tool2d
2022-11-08 15:00:31 +08:00
SSE2 加法:451 K 指令 /s
SSE2 减法:451 K 指令 /s
SSE2 乘法:452 K 指令 /s
SSE2 除法:118 K 指令 /s

除法是所有数学运算里最慢的。反而是现代 CPU 把乘法优化到和加法一样快,这是我没想到的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/893510

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX