Code

显示标签为“Programming”的博文。显示所有博文
显示标签为“Programming”的博文。显示所有博文

2011年10月27日星期四

Wrapper Objects & Object Reference

In JavaScript, primitive types behave like objects. They use dot expression to access certain methods. For example

var str = "abcd";
console.log(str.length); //4


But for object, every property is mutable. If we change the length to 10, it should be changed to 10. However, this is not the case.

str.length =10;
console.log(str.length); //4


This is because, primitive types don't actually have methods or properties. The access of those methods or properties invokes creation of their Wrapper Objects, which are String(), Numeric() and Boolean(). So when str.length is evaluated, something like this line is invoked.

var tmp_str = new String(str); return tmp_str.length; delete tmp_str;

After tmp_str is returned, it is destroyed. This is a wrapper object. It abstract the actual behaviours of primitive types, making it like a read-only object. Attempting to add/modify method or properties of primitive data types results in no effects. (because changes are only made to wrapper objects which are destroyed after access)

Another thing I learned about JS today is that, all objects in Javascript are "pass by reference". All primitive types are "pass by value".
Array are objects. so
var a=[1,2,3];
var b=a;
a[2]=0;
console.log(b); //[1,2,0]

So if there is a need to copy an independent object from another object, we have to write our own copy function.

function copyArr(a) {
    var b=[];
    for (var i=0;i<a.length;i++) {
        b[i] = a[i];        
    }
    return b;
}
var a=[1,2,3];
var b=copyArr(a);
a[2]=0;
console.log(a); //[1,2,0]
console.log(b); //[1,2,3]

2011年10月25日星期二

JavaScript Closure

因为新工作的缘故这两天开始学习JavaScript。
一直在看David Flanagan的JavaScript: The Definitive Guide 6th Edition,收获颇丰。

以前在云风的博客上第一次看到closure这个词的时候就很好奇,但那篇介绍是以C作为例子,讲的也不是很清楚,所以一直都没搞懂closure这个东西。
现在读JavaScript,对这个closure算是有一定理解。
Closure 中文叫做闭包,在JavaScript里函数都是有闭包的特性。

理解闭包必须分两步:
1.JS里面函数是first-class function,就是说函数跟class是一个级别的。 函数可以直接赋值到变量上,它的类型为"function",这个变量可以直接用来invoke函数。
function test1() {
    console.log(1);
}
func_var = test1;
console.log(typeof func_var); //"function"
func_var(); // invoked test1 -> '1'

2.JS使用lexical scoping,也就是一个函数的scope在它define的时候就确定了,而不是在invoke的时候才确定。

function test2() {
    var b = 1;
    return function () {console.log(++b);};
} 
b = 10;  //无影响
test2()(); //2
test2()(); //2
func_var = test2();
func_var(); //2
func_var(); //3
func_var(); //4

可以观察到,如果直接从运行从test2那里得到的函数,得到的结果会一直都是2。因为,每次运行test2,一个新的context被建立,一个新的b在这个context里面被建立。每次返回的匿名函数被挂接在一个新的context里面,所以它使用的b的值一直都是1。
然而,将test2返回的函数赋值到func_var上的时候,被返回的匿名函数原本的context被保存,就像把这个context空间封闭住了,只有,这个匿名函数可以访问。所以每次这个func_var被运行的时候都会只用之前旧的b,它相当于这个函数的私有变量了。
这种closure使var b成为test2这个object的私有变量,把data hide起来,不受其他scope的影响。可以猜想,JS使用这种特性可以实现object-oriented programming。

PS: from SICP about first-class procedures
In general, programming languages impose restrictions on the ways in which computational elements can be manipulated. Elements with the fewest restrictions are said to have first-class status. Some of the ``rights and privileges'' of first-class elements are:
  • They may be named by variables.
  • They may be passed as arguments to procedures.
  • They may be returned as the results of procedures.
  • They may be included in data structures.
Lisp, unlike other common programming languages, awards procedures full first-class status. This poses challenges for efficient implementation, but the resulting gain in expressive power is enormous.

Javascript is like Lisp very much in this aspect.

2011年9月24日星期六

Compiler difference between VS .net and VS 2008

Just found a problem today when I convert Visual C++ .NET Project to Visual C++ 2008.

In Visual Studio .NET, this is legal:

for (int i=0;i<10;i++) {} for (i=0;i<20;i++) {}


But this will generate a undeclared identifier error by VS 2008 compiler.

There may be some other error. So be cautious when converting projects between different version of VS.

2011年4月11日星期一

小结优化

貌似这学期所做的事情都是围绕优化这个问题来进行的。那我就来总结一些我接触的优化方式吧。这些优化方式都是从具体的事物上产生,但都可以抽象化到很多地方。
Hierarchical Structure
这个主要说的是事物组织的方式。如果是有等级划分,从大到小,从粗到细,从高到低的话,一个复杂的事物就可以运行的井井有条,这主要要归功于高效的检索。比如说,用Binary Partition Subdivision 做occlusion culling, 如果上面的node 不可见那么下面的node就不用再看了,这样一下子就可以省掉很多多余的运算,很快就能找到哪些是可见的多边形。其实这里的高效是利用了Binary Tree的高效,类似的有Quad Tree和Oct Tree.
Pre-Computation
把所有可以预先计算的东西全都在线下算好,然后在运行程序的时候就可以直接拿来用。这种思想使得很多复杂的东西都可以实时表现出来,比如说3D动画。那些灯光效果可能是用渲染农场渲染了好几天才完成的,放映出来却也就只有好几十秒的事情。但效果是相当的逼真。所以预先计算应该是提高性能所必需的。
Data Compression
现在的电脑磁盘大到不行了,但有一个问题是传输速度还是很慢,一般的就只有20多MB/秒。所以这是一个瓶颈问题。类似的问题还有网络带宽,特别是手机无线网络。解决这些问题的方法可以是将所需要的数据压缩。具体用什么样的方法,格式压缩,需不需要加密的问题就要因人而异了。Quake 的一个一般大的地图,几千个多边形,用run-length compression之后的Bit-Array PVS只有不到3MB。简单的加密可以是xor如果要安全点就要更麻烦了。
Perception-based/Interest Set
这些看起来可能只适合游戏,因为对于一些可信度高的应用来说,是不能对用户的perception也就是感知作出一些假设的。但这里所说的意思其实是,如果有太多的信息,我们是没有必要把所以信息都表现出来,因为用户不能一下子把所有东西都看到。这里其实包含这比较多的UI设计,如果是做应用软件。好的UI设计也是可以很好的减小计算量,而这些在手机上尤为重要。
Adaptive/Progressive
如果一下子不能把想要的东西计算出来,可以先大概的预计一下,勾勒出一个框架,然后再在之前框架的基础上重复计算。之所以要把这个框架先表现出来给用户看,一是可以让他很快的了解结果,二是如果他从框架很明显看出趋势不对得不到想要的结果,就可以直接取消计算,节省资源。比如在radiosity里面使用的Jacobian iteration 每一个轮回都比前一个要更靠近真实值。
Fast Memory
CUDA里面对于不同层次内存的使用是非常讲究的。但一般是速度快的内存都有这样那样的局限比如小或者是只读,所以如何有效的分配内存是一项很深的技术。需要对系统的深入了解,比如BUS 宽度,数据宽度,有几个计算核,每个核的分配是怎样的。如果内存分配恰当,是可以达到几十,几百倍的提速。
Approximation
有时有些问题很难甚至无法解决,我们需要用估算来求出一个大概的答案。估算不是准确的答案,但近似于准确。它不需要很多资源来创造,是很多学术上解决问题的一个方法。

2011年3月5日星期六

Coroutine


以前也听说过coroutine这个词,当时以为只是helper function的另外一种说法,故没有去研究个所以然。后来做unity3D project的时候有一次看到这个词,在文档里记录的例子是一个等待的程序,如下:

yield WaitForSeconds(5.0);


这个看起来不就是一个简单的wait吗,用一个loop一个timer就可以实现的一个东西而已。我当时这么认为。unitydoc也不是很详细,所以我也就没再去看。可是后来所UDK的时候遇到了一个叫latent function的东西,类似multi-threading吧,可以独立运行,然后在一段时间后返回。latent function也可以实现像sleep这样的功能。这时候我就想难道这个跟coroutine有什么关系,难道这是一个所以游戏引擎编程共有的功能。


UDK的文档里这样描述latent function:



While an actor is executing a latent function, that actor's state execution doesn't continue until the latent function completes. However, other actors, or the VM, may call functions within the actor. The net result is that all UnrealScript functions can be called at any time, even while latent functions are pending.


In traditional programming terms, UnrealScript acts as if each actor in a level has its own "thread" of execution. Internally, Unreal does not use Windows threads, because that would be very inefficient (Windows 95 and Windows NT do not handle thousands of simultaneous threads efficiently). Instead, UnrealScript simulates threads. 


这样看来我可以把coroutine或者latent function理解成一种模拟更高效的线程。在lua文档里对coroutine解释的很详尽。coroutine的一个关键词是yieldyield相当于一般程序里面的return。区别就在于return是将原来方程的stack context都清除了,下次call这个方程的时候,一个新的stack layer又会建立。而yield把执行权交给calling function同时也save自己的context,下次再被call的时候可以接着之前的context执行。

这样可以模拟多线程。试想有好几个子方程被一个母方程控制在一个loop里面,然后子方程又在自己的loop里面完成一些任务,每loop一次call一次yield。母方程里的loop按顺序resume子方程,这样母方程每loop一次,子方程也一次loop 一次。

coroutine比用thread的好处是coroutine不需要管理多线程会遇到的一些问题,比如说锁。但坏处我觉得是不能做太复杂的东西,因为这样会消耗大量的stack space,而且管理起来会很复杂。其实一些小的任务用coroutine就绰绰有余了,比如说walk()

UDK里的latent function应该是coroutine的一个wrapper。它的功能比unity里的coroutine(貌似只能wait)多了两个,MoveToFinishAnim 在使用这些function的时候我完全可以把它当作一个thread,但它是怎么实现的却是不得而知的。



2011年2月26日星期六

Just a note on CUDA AI setup

1. need to disable TDR: can just diable inside parallel nsight
2. need to disable WPFHDRAcceleration http://msdn.microsoft.com/en-us/library/aa970912.aspx

2011年1月16日星期日

About FYP

My FYP hasn't had any progress for a while already. I should say I was kind of unfamiliar with my code after December holiday, so a lot of time now are spent reading code and documentations. My current task is to implement the sleep function in client side. This requires me to look at client code, and turn off wifi card there when server command is sent over. The wifi card can easily be turned off on Linux, so I have to switch to Linux now.

Linux is in fact a very good thing. Although, it doesn't have very powerful tools, its simplicity complement any disadvantage it has. For example, making a system call is just one line of code. Its default packages are so developer friendly that, I can compile code in any way I like easily. Of course, the pre-requisite is I understand the working of the system. 

So basically what I want to do for my FYP is to turn off wifi for certain duration when command is sent over. It's of course a line of code to do that in Linux.
system('rfkill block wifi)')
But before that, in fact I have to do many things. Here are some I need to figure out:

  • send command from server to client
  • simulate client packets when his wifi is turned off
  • store sleep information and time on both server and client
  • switch on and off sleep state on both server and client side
The previous guy who were doing similar algorithm actually left some code I can refer to. But it seems to me that his code is not so reliable. This is because he didn't do off-line packets simulation and he put sleep information inside the client connection structure. Moreover, he didn't discover the PVS algorithm, so I assume he didn't really understand the Quake3 code before doing anything. Though his code is unreliable I still benefit from reading his code in some way. 

For example, Quake3's makefile is huge, and I really have no idea where to add my scan file in. His makefile did help figure out this. And he's also use SV_SendServerCommand function, this conform with my observation. However, he receive command at the wrong place and didn't acknowledge the command thus, will cause overflow hence leading to disconnection. So I correct this by putting command reception inside CL_GetServerCommand. So I have fixed the 1st problem. 

The 3rd, and 4th problem seems easy for me, I should solve those first. Then I should attempt the 2nd. Hopefully, Quake3 code has implemented some function to do off-line packets simulation, so I can make use of it. Otherwise, I would be troublesome to do playerState update on my own. 

It seems that these stuff is pretty easy to do, but that's not true. It really takes me a while to fully understand the Quake3 architecture. Michael Abrash's 'The Big Picture' did help me a lot. And I like what he said, "There are a million ways not to finish a project, but there's only one way to finish: Put your head down and grind it out until it's done." It inspires me when I feel frustrated. 

There still lies a long road to go for my FYP. The goal is to make my algorithm work perfectly without any glitch. By then, I should have fully understand the Quake3 code. One part of Quake3 that pose great interest to me is the renderer part. I am only able to spend a little time looking at the render. Though it uses OpenGl, very few native gl functions are actually exposed. Or maybe, I haven't found the correct place to look at. I will look at that after this project. 

2011年1月9日星期日

zt (bookofhook) : = The Quake3 Networking Model =

This article is a reprint/updated version of an e-mail I sent out to the mud-dev mailing list a few years ago, describing the Q3 networking model as described to me by John Carmack.  I did this partly out of my own curiousity (since I was firmly entrenched in the graphics side of things) and partly out of a desire to propagate information on the 100% unreliable networking model in Q3 which I felt (and still feel) was fairly groundbreaking due to its simplicity and ease of understanding.

== The First Attempt (QTEST/Quake2) ==

Carmack's first real networking implementation, back in 1995, used TCP for !QuakeTest (QTEST).  This was fine for LAN play, because the large packets (8K) wouldn't fragment on a LAN (by "fragment", I mean to the point where disassembly and reassembly induced significant overhead), but didn't work so well over the Internet due to fragmentation (where dis/reassembly and lost packets often resulted in very expensive resends).  His next iteration involved using UDP with both reliable and unreliable data, pretty much what many would consider a standard networking architecture.  However standard mixed reliabled/unreliable implementations tend to generate very hard to find bugs, e.g. sequencing errors where guaranteed messages referenced entities altered through unreliable messages.

== Quake3 ==

The final iteration (Quake3), which was the first time he really felt he "got it right" (whereas Carmack always felt a little uneasy with previous implementations' fragility), used a radically different approach.  With Quake3 he dropped the notion of a reliable packet altogether, replacing the previous network packet structure with a single packet type -- the client's necessary game state.  The server sends sequenced game state updates that are delta compressed from the last acknowledged game state the client received.  This "just works".  Dropped packets are handled implicitly, and specific commands are never acknowledged independently -- last known state acks are piggy backed on regular incoming/outgoing traffic.

The client's receive logic boils down to:
{
      if ( newState.sequence < lastState.sequence )
      {
        //discard packet
      }
      else if ( newState.sequence > lastState.sequence )
      {
         lastState = deltaUncompress( lastState, newState );

         ackServer( lastState.sequence );
      }
}

The client then extrapolates movement and whatever other information it needs based on the last game state it received.

It's even simpler on the server:
{
      deltaCompressState( client.lastAckState, newState, &compressedState );

      sendToClient( client, compressedState );
}

The server never sits around waiting for an acknowledgement. As a result, latencies are much lower than if you have code that sits there twiddling its thumbs waiting for a synchronous ACK.

There are two downsides to this implementation. The big one is that it soaks more bandwidth since the server is constantly pumping new state to the client instead of just sending new state when it doesn't get an ACK. Also, the amount of data sent grows with the number of dropped packets since the delta grows as a result.

The other downside is that the server must buffer a lot of data, specifically of the last acked state to the client (so it can delta compress) along with a list of all the previous states it has sent to the client (back to the last acked one). This is necessary so it can rebase its delta comparisons on any of the game states that the client has acked.

For example, let's say the client last acked sequence 14. The server is sending out new game states with incrementing sequences. It may be up to sequence 20 when it gets an ack from the client that it has received sequence 17. The server has to have the state that was sent as part of sequence 17 in order to rebase the delta compression, so if game states are large enough or the buffers are long enough, this can grow fairly high (to the tune of several hundred K per client).

All reliable data that another node needs is sent repeatedly until the sender receives an update for most-recent-ack (indicating that the packet has been received). For example, if a player sends a chat message (reliable) with update 6, he will continually send that chat message on subsequent state updates until he receives notification from the server that it has received an update >= 6.  Brute force, but it works.

== Port Allocation ==

A note about using multiple ports -- there is one significant advantage to using multiple ports, and that's that the OS buffers incoming data per port. So if you have a chance of a port buffer overflow, then multiple ports may not be a bad idea. If a port overflow is highly unlikely to occur, then multiple ports probably just complicate matters.

Speaking of port buffer overflows, John doesn't think this is a problem. People that spin a second thread just to block on incoming ports are probably just doing extra work that doesn't need to be done. Effectively a thread that pumps the data ports is duplicating the exact same work the network stack is doing in the OS. Instead, John just pumps the network stack at the top of the event loop. Goes to show that brute force sometimes "just works". On a listen server (i.e. you're hosting a server on your client), where frame times can be several dozen milliseconds, there is a chance that you'll get a buffer overrun. In that case there are some options in Q3 to minimize buffer sizes, etc. to reduce the likelihood of a buffer overrun.

One nice thing about Q3's networking architecture, however, is that even in the case of a buffer overrun it just keeps working. There is no effective difference to this system between a dropped packet and a buffer overrun; as a matter of fact, this may actually be masking some real buffer overruns, but so far Carmack's pretty sure that he's not even come close to hitting an overrun situation. Of course, the dynamics of a shooter are different than those of an MMOG (fewer clients, much higher bandwidth per client), but it's interesting nonetheless.

== NAT ==

One interesting note on NAT: apparently some older routers will randomly switch the client's port when behind a NAT. This is very rare, but causes lost connections "randomly" for many users. It took forever to track this down, but once it was discovered he solved it by having a client randomly generate a unique client-id (16-bits) at connection time that is appended to every incoming packet. That way multiple clients with the same IP can work just fine, even if their ports get re-assigned mid-session. Humorously enough, he never bothered handling the (so rare as to be insignificant) case where two clients behind the same firewall randomly generate the exact same ID (in the event this occurred one of the clients would be dropped due to badly out of sync state).

== Compression, Encryption, and Packets ==

Aside from application level delta compression, Q3 also does per-packet Huffman compression. Works great, simple to implement, and the upside to something more aggressive is very low.

He has done some half-hearted work on encryption, but basically app/stream-level encryption is pointless because of the sophistication of hackers. In the future he'll probably rely on higher level inspection (a la Punkbusters for Counter-Strike) instead of cute bit-twiddling.

Finally, I asked about packet size. He thinks the notion of a 512-byte optimal MTU is pretty much at least 5 years out of date. He sees almost no problems with a max packet size of 1400 bytes, and fragmentation just isn't an issue. During the course of typical activity, the actual payload size is often smaller than the UDP header size (!), and even
during hectic activity the packet size is on the order of several hundred bytes. The large packets (up to 1.4K) are typically when a large amount of game state must be sent at once, such as at connection time when there is no state to delta against and when there is a lot of startup initialization going on.

== Summary ==

The Q3 networking model obviates the need to even have a discussion about UDP vs. TCP, unreliable vs. reliable, and out-of-order vs. in-order. It's all unreliable UDP delta compressed against the last known state.

A very elegant and effective architecture, albeit one that is ideally suited to a subset of game types.

xor Encryption

Basically it use a key string to obtain cipher text by doing byte-based exclusively OR with original text cyclically. One more round with cipher text will restore the original text.  Very easy to implement, thus widely used.




This program can be cracked by frequency analysis. But that's only limited to alphabetic text. For thing like images and sound, it's hardly cracked.

2010年9月25日星期六

有关自己写常用的数据类型

这两天为了一个vsc++ priority queue 的bug浪费了不少时间。我的pqueue是用来储存struct的pointers。我用下面这种方法创建:

priority_queue<CellNode*, vector<CellNode*>, M_compare> openq;
M_compare也是用常规方法写的,跟官网上完全一样的格式。但我run程序的时候总是给我invalid heap的错误。网上搜一下后好象是说queue里的东西没有initialize或者是queue里的container access invalid address。然后我用vs debugger trace stack,发现在call了好几次我的comparator之后comparator的parameter会变成一个很大的负数,我觉得非常奇怪。看ms debugger源码里的那些程序感觉也不是很对,就很是抓狂。

最后觉悟了,我要的无非是一个简单的priority queue,我不需要很有效率的检索,因为我的数据集不是很大,完全没有必要用heap这么复杂的structure来实现。我也不需要通用,因为就只有我一个人用。 然后我就花10分钟写了一个很简单的pqueue。我用list来做container因为他的insert和remove比较快。在pop()里面我就用了一个linear search来找我的head,这样的complexity也就只有O(n)而已。push()和empty()也就是list自己的push_back和empty。额,感觉这个太简单了。用了一下,run without any error!

用vsc++里面的data structure,好处是不用自己写。但是出问题了是很难debug的。所以今后小的data structure可以先考虑自己写,也许节省的就不只两天的时间了。

2010年9月11日星期六

About Epsilon

前几天去cs4213的lab,做了一个dotproduct的问题,然后跟cosine对比结果。我当时用了"==",对比结果老是不对然后问TA。他说要用epsilon,我不知道是什么,问了好几遍什么是epsilon。最后TA懒得跟我解释就说,你不能这样直接比较,会有error,然后我就用">"比较,最后终于弄对了。

今天突然想起那天他说的epsilon,然后查了一下。原来这是一个学computing都应该知道的东西(汗~~)。这个跟floating point 的precision有关系,下面是从msdn上摘下来的:

Output
The difference between 1 and the smallest value greater than 1
 for float objects is: 1.19209e-007
The difference between 1 and the smallest value greater than 1
 for double objects is: 2.22045e-016
The difference between 1 and the smallest value greater than 1
 for long double objects is: 2.22045e-016

这是machine representation for floating number的局限性。又查了一下,比如说0.1011用十进制是 1/2 + 1/8 + 1/16也就是0.6875。机器能够精确表示的浮点数只有像0.5, 0.25, 0.75, 0.625...和他们的linear combination。这样float是32位,double是64位,long double是128位,他们能表示的最小数就不一样了。

所以在比较浮点数的时候要计算误差,这个误差就叫做epsilon。误差在一定的范围内了,就可以看作是相等了。

从上面可以看出来,float type的误差是在0.0000001和0.0000002之间的。所以epsilon应该是大于0.0000001的,不然就有可能永远无法接近正确值。    这个也是从网上找来的:

#define EPSILON 0.0001  // Define your own tolerance
#define FLOAT_EQ(x,v) (((v - EPSILON) < x) && (x <( v + EPSILON)))
int main()
{
  float a = 2.501f;
  a *= 1.5134f;
  if (FLOAT_EQ(a, 3.7850)) cout << "Expected value" << endl;
  else cout << "Unexpected value" << endl;
}

总结就是,今后做floating point comparison要用epsilon。

2010年9月9日星期四

static variables

今天花了一天的时间教自己了static variable

  1. static variable 自动 initialize to NULL(0)
  2. static variable can pointer to a constant or variable. When pointing to a variable, that variable must be static. Same rule applies to that variable.
  3. when return a value to a static variable from a function or method, the return value should be assigned to a static variable in that function.
  4. VC++ won't tell you your static variable contains rubbish, when it points to a non-static variable.


很简单的一个问题,弄了我一天。这个在eclipse 里面用java写都会有错误提示。

2010年9月8日星期三

有关**ptr的问题

今天恍然大悟,发现昨天想到有关pass **pointer的问题是错误的。

在function 的参数里面我们一般都是pass *ptr,这样比较安全,为什么呢?因为这样只是pass最初那个pointer的value,所以function里可以自己在做一个pointer 然后assign到这个value,然后就可以用了。原来那个pointer很safe,没有被更改,也无法被access到。

然后有一种情况我们需要access原来的pointer,比如要destroy一个queue,这时候需要free所有的指向queue的pointer,这时候就要用两个** pass pointer的address到destroy function里了。这样,原pointer就被完全操纵了。如果不是destroy action,这样pass pointer是非常危险的。如果你改变了它的位子,你就有可能找不到源数据的起始位子了。

昨天我还以为两个**会比较安全,甚至认为c++里面的reference variable也是这样implemment的。今早仔细一想,原来这是不对的。

最近好多deadline,有点stressed了。

2010年7月19日星期一

Bitwise Operation

今天看了一下 bitwise operation的用法与用处,在这里总结一下。

主要语法有四种 &,|,^ 和 <<(>>)
所谓bitwise也就是他们提供给他们的数字都是转化成2进制来计算的。
and(&)
x AND 0 = 0
x AND 1 = x
用法:x = x & 0x5
or(|)
x OR 0 = x
x OR 1 = 1
用法:x = x | 0x5
xor(^)
x XOR 0 = x
x XOR 1 = ~x
shift(<< or >>)
也就是把2进制的往左或者右移动一定数位。
左右补位都用0。
用法:x = x << 2 Bitwise operation的用处有好几个: 一是在计算图像颜色的时候。一般的颜色是32-bit,也就是高8位是alpha,然后是8位红,8位绿,底8位蓝。所以要提去某一种颜色,用一个and去mask一下然后再shift相应的位数就行了。如果要clear一个颜色,用提取颜色的mask的1s complement去mask一下就行了。如果要set一个颜色用or比较efficient。 另一个用处是在用作function flag的时候来轻便的获得状态。比如 #define ANIM_LOOP 1 // (0000 0001) #define ANIM_ONCE 2 // (0000 0010) #define ANIM_MAXSPEED 4 // (0000 0100) #define ANIM_MINSPEED 8 // (0000 1000) #define ANIM_CUSTSPEED 16 // (0001 0000) #define ANIM_LINK 32 // (0010 0000) #define ANIM_LINKALL 64 // (0100 0000) 注意define状态的时候一定要是set 一个bit,而且不能跟其他的状态相同。 Animate(lpdds, 8, ANIM_LOOP | ANIM_MAXSPEED | ANIM_LINK); call的时候就可以得到一个有多个bit set的二进制数。 int Animate(LPDIRECTDRAWSURFACE lpdds, int nFrames, DWORD dwFlags) { // test for looping if ((dwFlags & ANIM_LOOP) > 0)
anim.bLoop = TRUE;

// test for maximum speed
if ((dwFlags & ANIM_MAXSPEED) > 0)
anim.nSpeed = MAX_SPEED;

然后用and test 状态所占的那个bit有没有set。

// ...and so on
}

我觉得这种方法很巧妙。

还有一种更巧妙的用途是在swap number的时候,用bitwise operation可以不用第三个变量。


//  Value of x                         Value of y
// -----------------------------------------------
int x, y;           //  0                                  0
x = CONST_A;        //  CONST_A                            0
y = CONST_B;        //  CONST_A                            CONST_B
x = x ^ y;          //  CONST_A ^ CONST_B                  CONST_B
y = x ^ y;          //  CONST_A ^ CONST_B                  CONST_A ^ CONST_B ^ CONST_B == CONST_A ^ 0 == CONST_A
x = x ^ y;          //  CONST_A ^ CONST_A ^ CONST_B        CONST_A
//  == 0 ^ CONST_B == CONST_B          CONST_A
//  CONST_B                            CONST_A


很牛逼我觉得。

还有一种用处是替代十进制计算,比较方便的有:1.乘以2的阶层 2 (shift left) 2.除以2的阶层 (right shift) 3.对2的阶层求余 (and 2的阶层 - 1)


其实所有计算都可以用bitwise来做,计算机在进行2进制计算的速度是非常快的,所以在C 游戏编程里面一般都会尽量用到bitwise operation。

另外:在C里面数值是不能直接用2进制来表示的,但可以写成16进制的。2进制的数字很容易写成16进制,这使得bitwise operation非常方便。
nValue = 0x3FC;
另外一些以前不是很注意的东西
2 bytes = 16 bits = 1 word
2 words = 32 bits = 1 dword

------------------------------------------------
PS: (2011/3/22)
如果n是2的幂, 那么(i/n) = (i>>log2(n)), (i%n) = (i&(n-1)).
这个property在用1D数组表示2D数据的时候非常有用。CUDA 和 QUAKE3里都有这样的optimization.