V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nnegier
V2EX  ›  程序员

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

  •  
  •   nnegier · 2022-11-08 10:36:01 +08:00 · 3069 次点击
    这是一个创建于 754 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看 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;
        }
    
    21 条回复    2022-11-08 15:58:05 +08:00
    weeei
        1
    weeei  
       2022-11-08 10:39:09 +08:00
    没有区别,这作者写的字面量都不统一。
    效果是一样的,个人代码风格而已
    最后 return 0 ,而不是 return 0.0f 就写的很随意。
    fds
        2
    fds  
       2022-11-08 10:45:05 +08:00   ❤️ 2
    谷歌一下呗。除法比乘法慢。代码中这里的可以确定结果的除法在编译时就算出来了,所以运行时就只用乘法,不会影响速度。
    https://stackoverflow.com/a/4125074/502851
    Maboroshii
        3
    Maboroshii  
       2022-11-08 10:47:29 +08:00
    可能更想表达 72 分之 1 ?
    ysc3839
        4
    ysc3839  
       2022-11-08 10:53:53 +08:00
    @fds 但编译器没有那么蠢吧?如果乘法更快的话为什么不优化成乘以倒数?
    mxT52CRuqR6o5
        5
    mxT52CRuqR6o5  
       2022-11-08 11:05:13 +08:00
    @ysc3839 c 的话肯定能靠编译器优化好不需要写的这么拐弯抹角,java 我就不清楚了
    dcsuibian
        6
    dcsuibian  
       2022-11-08 11:05:17 +08:00 via Android
    @ysc3839 算倒数,还得先除
    maggch97
        7
    maggch97  
       2022-11-08 11:05:45 +08:00
    @ysc3839 因为这样优化会有精度损失
    maggch97
        8
    maggch97  
       2022-11-08 11:12:31 +08:00   ❤️ 2
    @mxT52CRuqR6o5 GCC 要做这个优化也是需要开 -ffast-math 的,而且一般是不会开这个编译选项的
    ysc3839
        9
    ysc3839  
       2022-11-08 11:15:32 +08:00
    @mxT52CRuqR6o5 Java 应该也有优化的,之前在网上听说 Java 等带虚拟机的运行时,长时间运行性能是能超越 C++ 这种编译好的程序的,因为虚拟机能在运行时统计性能数据并优化代码。

    @maggch97 这个解释是最合理的。
    shijingshijing
        10
    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
        11
    leonshaw  
       2022-11-08 11:21:19 +08:00
    @ysc3839 浮点数这两个不等价
    star9029
        12
    star9029  
       2022-11-08 11:26:36 +08:00
    clang -O1
    float test(float x) { return x / 2; }
    优化成 x * 0.5
    这种优化在 native 语言都是基操,java 就不清楚了
    maggch97
        13
    maggch97  
       2022-11-08 11:43:45 +08:00   ❤️ 1
    @star9029 /2 跟 /72 是两个问题,https://godbolt.org/ 在这上面玩一下看看吧
    maggch97
        14
    maggch97  
       2022-11-08 11:45:44 +08:00
    编译器开到 -O1000 都不会把 /72 优化成 *1/72
    star9029
        15
    star9029  
       2022-11-08 11:52:09 +08:00
    @maggch97 感谢指正,我没留意到这个优化有区别
    ho121
        16
    ho121  
       2022-11-08 11:54:24 +08:00
    ```
    >>> 111 / 72
    1.5416666666666667
    >>> 111 * (1/72)
    1.5416666666666665
    >>>
    ```

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


    icelake 也有一个 div
    shijingshijing
        19
    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
        20
    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 把乘法优化到和加法一样快,这是我没想到的。
    xz410236056
        21
    xz410236056  
       2022-11-08 15:58:05 +08:00
    @shijingshijing #19 确实,刚刚看了一下 https://uops.info/table.html 吞吐量和延迟仍然有差距。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2593 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 03:06 · PVG 11:06 · LAX 19:06 · JFK 22:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.