编程中用移位运算替代乘除法会不会有问题?

2018-07-10 14:40:33 +08:00
 jssyxzy

因为移位运算实际上有算术移位,逻辑移位,循环移位;
又有有符号数,无符号数;
不同编程语言有可能实现不同。

请问这里面有没有什么坑?

10262 次点击
所在节点    程序员
80 条回复
tempdban
2018-07-11 23:22:44 +08:00
@yanaraika 用 O2 编就好了啊,为何不敢,在线上的还没出过事故呢。
zhidian
2018-07-11 23:31:28 +08:00
可以用位移,但不能是为了加速乘法。你要弄一个 mask 啥的,当然可以用位移。
tlday
2018-07-12 00:03:27 +08:00
@jssyxzy 我也没搞清楚你在纠结啥,你问的明明就是一个工程问题,却非要说是科学问题。大家热心回答你的问题,答案你不满意,你就甩来一句“没见过世面”,大家伙欠你的啰?
lance6716
2018-07-12 00:51:02 +08:00
这么牛逼怎么不知道看看语言 ref
jssyxzy
2018-07-12 00:59:58 +08:00
@tlday 那我来告诉你纠结什么,我只想知道这个究竟是否完全等价,转换有什么可能出现错误的地方,所以我说这是科学问题;但是一堆人回答我,我当然知道他们说的是什么意思,说的是可读性和可维护性么,所以他们说的是工程问题。
如果不能理解,我换个说法,我问二叉树实现的某个细节问题,一堆人回我,这个用不到,高级语言都封装了,直接调 api 就行了,然后告诉我怎么用 api,你觉得我什么感受。
当然我的题目歧义很大,我指的是我自己的私人的编程,可能大多数工作的都会理解自己工作的编程。
jssyxzy
2018-07-12 01:02:59 +08:00
tempdban
2018-07-12 01:15:32 +08:00
@jssyxzy 我也有点这个感受
mingl0280
2018-07-12 08:39:44 +08:00
@jssyxzy 有可能,自己写会有各种意想不到的细节问题……比方说位丢失啊什么的乱七八糟的问题……
fcten
2018-07-12 10:21:08 +08:00
在原生支持整数操作的语言中,一般来说在没有溢出的情况下都是等价的。但是在很多“高级”语言中,位运算往往是模拟的,这种时候是否等价就和该语言的具体实现相关了。

比如说 JavaScript,因为 JavaScript 中 number 是只有浮点型的,所以 JavaScript 的位运算只能在浮点数上操作。由于浮点数操作上的一些坑,你会得到以下结果:

(0.7+0.1)*10 << 1 = 14
(0.7+0.1)*10 * 2 = 15.999999999999998
jzq526
2018-07-12 11:07:21 +08:00
可以,但没有必要,一则是代码可读性降低,二则要兼顾不同的平台(如果考虑移植的话),三则可以通过位移实现的乘除法,编译器可以自己优化,去年看的一本书上说的
wsy2220
2018-07-12 11:11:54 +08:00
谁在我这里用,我打死他
x86vk
2018-07-12 11:12:33 +08:00
@smdbh 做快速幂也是可以的。
mandy0119
2018-07-12 11:30:57 +08:00
大概科学都不考虑代码可读性的问题吧。我们这边位运算只有表示状态更改状态时候用
yanaraika
2018-07-12 11:49:13 +08:00
@tempdban O3 也是完全符合标准的。O3 会 segfault 说明代码中有未定义行为,O2 正常运行也许只是“表现得”正常,实际哪里多写了一个字节、在某些特定输入下就会炸这种定时炸弹是比起崩溃更加不稳定的因素。我们团队这边都采用 O3 编译,一旦出现问题必须及时定位,尽早发现问题而不是掩盖问题
tempdban
2018-07-12 13:03:32 +08:00
@yanaraika O3 符合标准,是谁的标准?
你怎么就能确定我们的代码经过 O3 优化后,编译出来的东西,就一定是我们想表达的意思?
tempdban
2018-07-12 13:04:26 +08:00
@yanaraika 我还是那句话,有的事情远比你我在这里讨论的情况,还要复杂
miketeam
2018-07-12 13:54:01 +08:00
我有个业务还是必须用移位操作。就是一秒发送 5000 个数据包,然后里面不断除法计算…
yanaraika
2018-07-12 13:56:32 +08:00
@tempdban 当然是 ISO/IEC 9899 与 ISO/IEC 14882。 如果不是你们表达出的意思,那么要么是编译器的 bug (小概率),大概率你们写的根本就不是 well-defined C/C++,这和编译不过属于同等严重的问题。当然,你们可以认为自己是 Linus,giving gcc a f*ck,但 Linux kernel 在 O3 下可是没有任何问题的;或者直接因为招不到靠谱 /足够的人接着在冰上走,这就和“编程”没有任何关系了。
tempdban
2018-07-12 14:37:20 +08:00
@yanaraika 其实我并不觉得写未定义行为的代码,就是问题,更谈不上是严重的问题。
我们这套代码用了十多年了,好多东西都是静态库直接分发的,能 O3 的部分早就 O3 了。
历史包袱有多重,你想不到的兄弟,就这样上边还嫌需求开发的慢呢,本来 O2 能跑,就为了一点虚无缥缈的性能,你非要换成 O3,线上炸了,这个责任谁接,炸了的这部分代码和你一点关系都没有? O2 加上特殊的代码写法,能达到我们要的性能,何必换呢?
再说 O3 又不是一定会带来性能提升,我贴一段话:

-O3 has several disadvantages:

First of all it often produces slower code than -O2 or -Os. Sometimes it produces longer code due to loop unrolling which may be in fact slower due to worse cache performance of code.
As it was said it sometimes produces wrong code. It may be either due to error in optimalization or error in code (like ignoring strict aliasing). As kernel code sometimes is and sometimes have to be 'smart' I'd say it is possible that some kernel developer made some error. I experienced various strange problems, like crashing of userspace utilities, when I compiled kernel with gcc 4.5 which at that point was stable. I still use gcc 4.4 for kernel and several selected userspace utilities due to various bugs. The same may apply for -O3.
I don't think it offers much benefit for the Linux kernel. The kernel does not do heavy computations and in places it does, it is optimized with assembly. -O3 flag will not change the cost of context switching or speed of I/O. I don't think something like <0.1% speedup of overall performance is worth it.

只要是能稳定,性能不差,O2 又能怎样。
招不到靠谱的人,兄弟,不是说这十几年里每个人都是会写 well-defined 的代码,要是人人都这样,那还要做什么测试,直接发布就得了。
理想很丰满啊哥们。
ligo
2018-07-12 22:00:40 +08:00
只有乘除 2^n 这个特例才可以,然而编译器会自动优化。

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

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

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

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

© 2021 V2EX