为你的变量们选择合适的数据类型将决定一个解决方案的正确与否。尤其是在几何问题上,精度问题通常导致了解决方案错误。更糟糕的是,有许多(通常不正确)关于这些问题的原因和解决方法的传闻。
要能够避免这些问题,需要了解一些计算机内部工作原理。在这篇文章中, 我们将给出一些必要的事实并且来反驳一些传闻。在读完和理解这篇文章的基础上,你将能够避免上面提到的问题。
这篇文章绝不是想要成为完整的参考书并且不是100%准确。其中有许多展示的东西都是简化过的。由于这篇文章的读者是TopCoder(TC)成员, 我们将关注与TC用来评估解决方案的基于x86架构的机器。例如,我们假设在我们的电脑中一个字节包含8个位并且机器使用32位的寄存器。
虽然大部分文章都是宽泛的可以应用与所有的程序设计语言,这篇文章则将侧重点转向C++并且包含一些情况下在g++中的注意点。
我们一开始将展示一张(经过一些简化的)在g++编译器上可用的整数数据类型表。你可以在任何g++的参考书中找到这张表。TC中其他所有的编译器有着类似的数据类型并且在对应的编译器文档中有着相类似的表。如果你没有用心记住,查查它们。下面我们将要解释所需要知道的每一个类型的存储大小,根据存储大小可以简单的推导出整数的范围。
| name | size in bits | representable range |
| char | 8 | -27to 27 - 1 |
| unsigned char | 8 | 0 to 28 - 1 |
| short | 16 | -215 to 215 - 1 |
| unsigned short | 16 | 0 to 216 - 1 |
| int | 32 | -231 to 231 - 1 |
| unsigned int | 32 | 0 to 232 - 1 |
| long | 32 | -231 to 231 - 1 |
| unsigned long | 32 | 0 to 232 - 1 |
| long long | 64 | -263 to 263 - 1 |
| unsigned long long | 64 | 0 to 264 - 1 |
注意点:
• int和unsigned int的存储大小是平台相关的。举个例子,在使用64位处理器的机器上,g++中的ints将具有64位。老的Borland的C编译器使用16位的整数。可以保证int总是至少具有16位。类似的,可以保证, 在任何系统上long至少具有32位。
• long long是g++的一个扩展类型, 它不是C++标准的一个部分(现在呢?)。许多C++的编译器舍弃这个数据类型或者用别的名字替代。例如MSVC++中有取而代之的__int64
传闻: 有符号整数存储时需要使用一个符号位和一个“数字”位
正确性: 部分正确
现在大部分电脑包括TC中使用的,以补码的形式存储整数。对于非负整数最高位是0而对于负数是1.但是这个不是一个符号位,我们不能得到一个“负0”通过翻转该位。负数以一种稍微不同的方式存储。-n存储的形式相当于n-1的按位取反。
在表2中我们将给出较小的以有符号字符型变量进行存储的位形式。
越靠右的位越是低位。
| value | two's complement form |
| 0 | 00000000 |
| 1 | 00000001 |
| 2 | 00000010 |
| 46 | 00101110 |
| 47 | 00101111 |
| 127 | 01111111 |
| -1 | 11111111 |
| -2 | 11111110 |
| -3 | 11111101 |
| -47 | 11010001 |
| -127 | 10000001 |
| -128 | 10000000 |
注意由于负数的存储形式,两边的数并不是关于0对称的.b个位能够表示的最大整数是2^(b-1) - 1,最小的数是-2^(b-1).
一个简洁一点看补码的方法是所有的数字都是以2为基的除了最大的2的幂次是负的。举个例子, 11010001 位模式相当于1*(-128) + 1*64 + 0*32 + 1*16 + 0*8 + 0*4 + 0*2 + 1*1 = -128 + 81 = -47
传闻: 无符号数的存储形式就是它对应的二进制形式
正确性: 正确
一般来说,位形式包含了代表这个数的2进制表示。举个例子,11010001位形式对应于1*128 + 1*64 + 0*32 + 1*16 + 0*8+ 0*4 + 0*2 + 1*1 = 209
因此,一个b位的无符号变量,能表示的最小的数是0,最大的是2^b - 1(对应与所有的1的形式).
注意,如果最左边的位是0, 那么这个形式所对应的值不管这个数是有符号还是无符号都是相同的。如果我们有一个b位的串,并且最左边的位设置为1, 并且表示的数是无符号数x,同样的形式的有符号的数的值为x-2^b.
在我们上一个例子中,11010001形式既可以表示209(无符号变量)也可以表示-47(有符号变量)。
传闻: 在C++中,代码"int A[1000]; memset(A, x, sizeof(A));"在A中存储了x的1000个拷贝
正确性: 错误
memset()函数填充以字符为单位的内存,而不是整数。因此对于绝大多数的x值你会得到不期望的结果。
尽管如此, 对于两个值: 0和-1,它通常能够工作(这个经常使用)。第一个情况很简单,通过使用0来填充整个数组,每一个int的所有位都变为0, 因此代表数字0.事实上,第二个情况和前面一样,-1用char表示是11111111,因此我们用1填充了整个数组,使得数组包含-1.
(注意大多数处理器都有一个特殊的指令来用一个指定值填充一块内存。因此memset()操作比通常的用循环来填充要快)
当你知道你正在做什么的时候, memset()可以被用来使用特大或
