KEEP K.I.S.S.

tk's blog

"a <= b" 和 "not ( b > a)"

今天在看《LUA程序设计(第2版)》时候,发现了一个比较有意思的问题。

在 13.2 节 关系类的元方法 中,作者解释道为什么要同时提供 小于小于等于 两个关系运算符的实现:

在 Lua 4.0 之前,所有顺序操作符(order operators)都被转化为一种操作符(小于),例如,a <= b 转化为 not (b < a)。不过,这种转化遇到“部分有序(partial order)”就会发生错误。所谓“部分有序”是指,对于一种类型而言,并不是所有的值都能排序的。例如,大多数计算机中的浮点数就不是完全可以排序的。因为存在着一种叫 “Not a Number (NaN)”的值。 IEEE 754 是一份当前所有浮点数硬件都采用的事实标准,其中将 NaN 视为一种未定义的值,例如 0/0 的结果就是 NaN。标准规定了任何涉及 NaN 的比较都应返回 false(假)。这意味着 NaN <= x 永远为假,但是 x < NaN 也为假。因此,前面提到的将 a <= b 转化为 not ( b < a ) 就不合法了。

就是说 关系运算符的结果并非都是真的表示两个运算数是可以比较的,例如 在 IEEE 754 标准中提到,所有涉及 NaN 的浮点数关系比较全部应返回 false,也就是说 NaN <= x 为 false,NaN > x 也为 false,所以 a <= b 和 not ( b > a) 并非完全等价。

另外一段叙述是针对扩展使用的

在上面的集合示例中,也存在着类似的问题。在集合操作中 <= 通常表示集合间的包含关系: a <= b 通常意味着 a 是 b 的一个子集。根据这样的表示,仍有可能得到 a <= b 和 b < a 同时为假的情况。因此需要分别为 __le(小于等于)和 __lt(小于)提供实现

这里举例,a、b 两个集合交集为空,并集不为空,则 a <= b 为假,而 b > a 也为假。

C 语言中的位取反操作符 续

前一篇文章的问题,在我查阅了《The C Programming Language》(K&R) 第二版后,发现了如下的定义

在附录A 参考手册的 A.7.4 一元运算符 二进制反码运算符 中有如下表述

一元运算符 ~ 的操作数必须是整型,结果为操作数的二进制反码。在运算过程中需要对操作数进行整形提升。如果操作数为无符号类型,则结果为提升后的类型能够表示的最大值减去操作数的值而得到的结果值。如果操作数为带符号型,则结果的计算方式为:将提升后的操作数转换为相应的无符号类型,使用运算符 ~ 计算反码,再将结果转换为带符号的类型结果的类型为提升后的操作数类型

对于整形提升,在附录A 参考手册的 A6.1 整形提升 有如下表述

在一个表达式中,凡是可以使用整形的地方都可以使用带符号或无符号的字符、短整型或整型位字段,还可以使用枚举类型的对象。如果原始类型的所有值都可用 int 类型表示,则其值将被转换为 int 类型;否则将被转换为 unsigned int 类型。这一过程称为整形提升(integral promotion)。

请注意红色标注的句子,“结果的类型为提升后的操作数类型”,意味着 ~(char) 、 ~(short) 等的结果类型都是整型(int)。

C 语言中的位取反操作符

话不多说,先来看一段代码:

 

 1 #include <stdio.h>
 2 
 3 int main(int argc, char *argv[])
 4 {
 5         unsigned char uc = 0x1f;
 6         unsigned int ui = ~uc;
 7         unsigned long long ull = ~uc;
 8 
 9         unsigned char cc = ~uc;
10         printf("uc = %#x\n", uc);
11         printf("cc: ~uc = %#x\n", cc);
12         printf("ui: ~uc = %#x\n", ui);
13         printf("ull: ~uc = %#llx\n", ull);
14         return 0;
15 }

因为用到了 C99 标准的 long long 类型,所以编译时候需要添加 "-std=c99" 选项:

gcc -Wall -std=c99 -o file file.c

输出结果(x86 mingw32):

 

uc = 0x1f
cc: ~uc = 0xe0
ui: ~uc = 0xffffffe0
ull: ~uc = 0xffffffffffffffe0
 
这里显现出了 位取反 操作的结果特殊性,对于通常而言,我们以为一个 unsigned char 位取结果的类型也应该是 unsigned char ,也就是说当我们把这个结果赋给一个 unsigned int (或者 unsigned long long)类型的值时候,这个值也应该是 不会超过 unsigned char 的取值范围的。但是上述代码显示出并非这样的情况。
 
上述代码显示出把 unsigned char 位取反值赋给 unsigned int 变量时,除了低位字节是补码外,高位字节都是 0 的补码 1了。对于赋值给 unsigned long long 变量也是如此。
 
我个人理解的解释是,在 x86-32 的机器上,机器字长为 32,寄存器长度也是32,对于像 unsigned char 这样的 1 byte 的值,运算时会存储在寄存器中的低位字节,高位字节都为0,然后位取反操作指令会对整个寄存器的值位取反,然后赋值给 unsigned int (4 bytes)这样的变量时,是将整个寄存器值取出。这样高位字节都会是 0 的补码 1。但是这样怎么解释赋值给 unsigned long long 类型变量发生的情况呢?或者说对于位取反操作的结果是个特殊状态的值?
 
PS: 最近在看 CSAPP(《深入理解计算机系统》)第二版,其中在做练习题 2.12 发现了上述情形,之前不知道,所以就研究了一下下,不过还是半解。。。。

<转载>我的一些“偏见”

转自:庄周梦蝶
作者:dennis

    在豆瓣发了一些牢骚,索性多说一些我个人对人对事的偏见,既然是偏见,就不会让人舒服,事先声明是扯淡,不想浪费时间的人略过。

1.我们要远离新浪微博,新浪微博跟twitter不一样,twitter是为了让每个人的信息的更好更快地传播而设计的,而新浪微博是为了让权威的声音更好更快地传播而设计的。迷恋上新浪微博,你要么是权威,要么是跟随权威。成为权威的,免不了沾沾自喜,真以为自己成了“权威”。更可怕的是你不可避免地要生活在相互吹捧和喧嚣中。

2.在编写代码之外,我们可能需要更多的手艺傍身,例如木匠或者厨师,以免在乱世的时候因为不需要程序员而饿死。ps.计算弹道轨迹的程序员除外。

3.据说真正的牛人从不跳槽,作为大多数不是牛人,以及已经远离牛人行列的我们(跳槽超过3次以上),跳槽仍然是你提升自己的有效途径,无论是薪水还是技术。

4.写简历的技巧,我慢慢领悟到了,少点技术术语,多点成效和应用,打动了HR过了第一关之后,再去跟技术人员扯淡。

5.简历要定时更新,你可以理解成定时提醒下猎头和HR,关注我啊,关注我啊。

6.强烈地拥抱文本化,配置文本化(没人会脑残地用二进制当配置文件吧?),协议文本化,婚姻文本化。

7.一切不以加薪为目的的挽留,都是耍流氓,这不是我的原创。

8.有趣比实用重要,没趣味的东西,给钱也不去做(好吧,我说假话)。

9.对新潮的东西保持一点警惕,如果这个东西三个月后还有人在谈论,那可以关注下

10.代码永远比文档、博客真实和靠谱,阅读代码习惯了,跟阅读文档没啥区别。

11.少关注博客和新闻,戒掉看google reader的习惯。现在更多地看maillist上的讨论和问题,真正重要的东西你永远不会错过。

12.不追求完美,等你完美的时候别人已经是事实标准。

13.大型的技术聚会不是为技术人员准备的,这是大公司给员工的度假福利和领导们的吹水时间。只有在小型的技术聚会上才能看到一些有价值的东西,任何稍微跟商业沾一点边的几乎都没有太大价值,我说的是国内。

14.80%的分享都只对演讲者有益,该sb的还是sb,该牛b的还是牛b。最有效的分享是结对编程和结对review。分享和培训最大的意义是让行政们觉的自己的存在价值很大。

15.国内翻译国外经典>国内原创精品>国外原版,这个原则对英语好的人除外。

16.极其讨厌要求强制缩进的语言,比如python。

17.标榜是一种人生态度,装B装久了你就真牛B了。

18.凭啥不造轮子,你们造轮子舒坦了,爽快了,就不让别人造了。我造轮子我快乐。

19.偏见不全是坏事,坏的是不愿意改变偏见。


    扯淡时间结束。

 

Simple, not simpler

好久没有写小结型的文章了,主要是这段时间以来感觉自己稍微有些混乱,思绪繁飞不得掌控。

这段时间主要是看《代码大全》(第二版),内容很细致,包括从思路设计到实际编码各个环节,有很多常用的技巧以及注意事项。

开始看 Lua 的相关书籍了,这主要是受到最近一直泛起的“simple”的思绪,渴望回归“简单”。《Programming in Lua》这书很不错,目前看到 chapter 7,重大的收获是 对于函数式编程的的一些较深的理解,比如 closure 和 tail call。

之前学习的 Ruby 在目前工作中最主要的用途还是 正则表达式,用来处理文本数据。主要是自己弱爆了,不想去学 grep,sed 这些更为适合的工具,不过好在 Ruby 用的也是 Perl 风格的正则表达式。Ruby 的主张是 Coding for fun, Ruby 很容易学,也很容易用,主要是因为 面向对象设计完全和多种多样的方法风格。我现在就主要靠 irb (Interactive Ruby Shell) 来做计算器,功能强大,方便实用。 [Θ▽Θ]

交叉看的其他一些书籍有《代码整洁之道》(看书名就知道是干嘛的了,书中的语气措辞我比较喜欢,偏激进)、《深入理解计算机系统》(真的是“深入”,好书不需要解释,看完前言估计你就很有胃口了)、《OpenGL 超级宝典》(偏向工具库方面。。。)

当前最大的问题是心态噪杂,难以平复,想沉浸如那种简简单单的状态却感到困难。看来自己当前悟性还是不够,也许是自己太过心急了。

贴一句爱因斯坦的话共勉:Make things as simple as possible, but not simpler.