Most machines encode signed numbers using a two’s-complement representation and encode floating-point numbers using IEEE Standard 754.
https://www.cnblogs.com/mrblug/p/5706996.html 浮点数上溢和下溢
(之前只知道整数溢出可能会造成两个正数相加得负数,对于浮点数上溢和下溢的情况并不了解)Integer representations can encode a comparatively small range of values, but do so precisely, while floating-point representations can encode a wide range of values, but only approximately.
A machine-level program views memory as a very large array of bytes, referred to as virtual
memory.
C指针的大小跟寻址空间位数有关,https://blog.csdn.net/FX677588/article/details/52700894。指针的类型会影响解引用、指针加减运算。机器的字长决定寻址空间位数。gcc -m32 prog.c可以指定以32位编译,所得可执行文件可以在32位机器运行。也可以在64位系统中运行(backward compatibility)。
long类型的大小也跟寻址空间位数有关。因此有了int32_t和int64_t,应尽量使用,以便移植。
bi-endian,可以支持大端或小端。当操作系统确定时,字节序也就确定了。如ARM处理器是bi-endian的,Android和IOS都是小端的。
C的字符串的存储情况与字节序无关,与字长也无关。
casting意为显式类型转换。思考:如何用C语言print出一个int的字节存储情况?(对int取址,转为unsigned char*型的指针,然后向后读4次)
实际上,数据类型仅仅是字节的解释。同样的字节存储,声明不同的类型,解释出不同的结果。将signed和unsigned进行互相转换,字节存储不会发生变化,但解释出的数值可能就不一样了。printf中的占位符也是一样的道理,对字节存储进行解释。
typedef 内置类型 自定义类型
Even identical processors running different operating systems have differences in their coding conventions and hence are not binary compatible.
In practice, however, almost all compiler/machine combinations use arithmetic right shifts for signed data, and many programmers assume this to be the case. For unsigned data, on the other hand, right shifts must be logical.
In contrast to C, Java has a precise definition of how right shifts should be performed. The expression x >> k shifts x arithmetically by k positions, while x >>> k shifts it logically.
The most common computer representation of signed numbers is known as two’s-complement
form. This is defined by interpreting the most significant bit of the word to have
negative weight. (个人认为这种理解比主流教材“最高位是符号位”的说法更容易接受。主流教材需要大家背诵“正数的补码是他本身,负数的补码是取反后加一”。相反数的和为0,这一点可以辅助记忆。)
When an operation is performed where one operand is signed and the other is unsigned, C
implicitly casts the signed argument to unsigned and performs the operations assuming the numbers are non-negative. This leads to non-intuitive results for relational operators such as < and >.
(常常遇到的warning:comparing between signed and unsigned。最简单的一个例子:-1>0U,因为-1会被转成unsigned型)
One way to avoid such bugs is to never use unsigned numbers. In fact, few languages other than C support unsigned integers.(作为系统语言,在flags、地址等场景unsigned还是有用的。)
符号扩展:
sx = -12345: cf c7
usx = 53191: cf c7
x = -12345: ff ff cf c7
ux = 53191: 00 00 cf c7
下图是一段有漏洞的代码,因为maxlen为负数时,被转换为无符号的size_t,然后就会很大。
1和2的区别,E=e-Bias和E=1-Bias(大E表示真正的指数,小e表示图中八位作为unsigned的值,bias用于修正范围,如float,1~ 254 -126~+127),M=1+f和M=f。见原书P143.
原书中有个12345.0的例子,先写出整数12345的二进制表示,然后在第一个1后面加小数点。小数点需要移动的位数就是e,e+bias得到E。而M就是小数部分。
四种取整模式,区别在于对1.50和2.50的取整。
10.00011 B = 10.00 B
10.00110 B = 10.01 B
10.11100 B = 11.00 B
10.10100 B = 10.10 B
浮点运算的一大问题是,不满足结合律(仅是abel群)。(3.14+1e10)-1e10和3.14+(1e10-
1e10)就是一个例子。这导致,即便是下面这样简单的编译器优化可能也不敢做:
x = a + b + c;
y = b + c + d;
The compiler might be tempted to save one floating-point addition by generating
the following code:
t = b + c;
x = a + t;
y = t + d;
浮点乘法不满足结合律和对加法的分配律(均由于INF)。
both unsigned and two’s-complement arithmetic satisfy
many of the other properties of integer arithmetic, including associativity, commutativity, and distributivity. This allows compilers to do many optimizations. For example, in replacing the expression 7*x by (x<<3)-x, we make use of the associative, commutative, and distributive properties, along with the relationship between shifting and multiplying by powers of 2.
浮点运算跟整型运算的一个区别是,整型运算可以认为是“首尾相连”的(上溢之后变成负数),而浮点运算不是(无论上溢多少都是INF)。
From float or double to int, the value will be rounded toward zero. The value may overflow. The C standards do not specify a fixed result for this case. Intel-compatible microprocessors designate the bit pattern [10 … 00] (TMinw for word size w) as an integer indefinite value.
Any conversion from floating point to integer that cannot assign a reasonable integer approximation yields this value. Thus, the expression (int) +1e10 yields -21483648, generating a negative value from a positive one.
the largest finite number that can be represented with double precision
is around 1.8 × 10^308.