我在二进制级别上考虑这个。
一个值为 1 的浮点数和一个值为 1 的整数不会编译成(这里省略很多零)
0001
如果他们都编译到这个,那么这种不精确性从何而来。
我正在使用的资源是http://www.cprogramming.com/tutorial/lesson1.html
谢谢。
我在二进制级别上考虑这个。
一个值为 1 的浮点数和一个值为 1 的整数不会编译成(这里省略很多零)
0001
如果他们都编译到这个,那么这种不精确性从何而来。
我正在使用的资源是http://www.cprogramming.com/tutorial/lesson1.html
谢谢。
这是可能的。浮点数以指数表示法 (a*2^n) 表示,其中一些位表示 a(有效数),而一些位表示 n(指数)。
由于所谓的鸽巢原理,您不能唯一地表示浮点值范围内的所有整数。例如,32 位浮点数超过 10^38,但在 32 位上,您只能表示 2^32 个值 - 这意味着某些整数将具有相同的表示。
现在,当您尝试执行以下操作时会发生什么:
x = 10^38 - (10^38 - 1)
你应该得到 1,但你可能不会,因为 10^38 和 10^38-1 彼此如此接近,以至于计算机必须以相同的方式表示它们。因此,您的 1.0f 通常为 1,但如果此 1 是计算结果,则可能不是。
这里有一些例子。
准确地说:如果整数的二进制表示不使用比浮点格式提供的尾数加上隐式一位更多的位,则整数可以精确地表示为浮点数。
IEEE 浮点数的尾数为 23 位,添加一个隐式位,您可以将任何可以用 24 位表示的整数存储在 a 中float
(即最大为 16777216 的整数)。同样,双精度数有 52 个尾数位,因此它可以存储高达 9007199254740992 的整数。
除此之外,IEEE 格式首先忽略奇数,然后忽略所有不能被 4 整除的数字,依此类推。因此,even0xffffff00ul
可以完全表示为浮点数,但0xffffff01ul
不是。
所以,是的,您可以将整数表示为浮点数,只要它们不大于 16e6 或 9e15 的限制,您甚至可以期望浮点格式的整数之间的加法是准确的。
如果小于某个数字,Afloat
将int
准确存储 a ,但如果您有足够大的,尾数中将没有足够的位来存储整数的所有位。然后假定丢失的位为零。如果丢失的位不为零,那么您将不等于您的.int
int
int
float
假设字母代表一点,0/1。然后一个浮点数看起来(示意性地)像:
smmmmee
其中 s 是符号 +/-,数字是.mmmm x 10 ^ ee
现在,如果您有两个紧随其后的数字:
.mmm0 x 10 ^ ee
.mmm1 x 10 ^ ee
那么对于大指数 ee,差异可能大于 1。
当然,在基数 2 中,像 1/5、0.2 这样的数字不能准确表示。对分数求和会增加误差。
(请注意,这不是确切的表示。)
与具有单一基本数字类型的一些现代动态类型编程语言(如 JavaScript 或 Ruby)相反,C 编程语言有很多。这是因为 C 反映了在处理器寄存器中表示不同类型数字的不同方式。
要研究不同的表示,您可以使用union
可以将相同数据视为不同类型的构造。
定义
union {
float x;
int v;
} u;
分配u.x = 1.0f
并printf("0x%08x\n",u.v)
获得 的 32 位表示形式1.0f
为浮点数。它应该返回0x3f800000
,而不是0x00000001
像人们预期的那样。
正如前面的答案中提到的,这反映了浮点数作为 32 位值的表示形式为 `
1.0f = 0x3F800000 = 0011.1111.1000.0000.0000.0000.0000.0000 =
0 0111.1111 000.0000.0000.0000.0000.0000 = 0 0x7F 0
这里三部分是符号s=0,指数e=127,尾数m=0,浮点值计算为
value = s * (1 + m * 2^-23) * 2^(e-127)
通过这种表示,可以精确表示从 -16,777,215 到 16,777,215 的任何整数。这是 (2^24 - 1) 的值,因为尾数中只有 23 位。这个范围对于许多应用来说是不够的,因此该float
类型不能替代该int
类型。
按类型精确表示整数的double
范围更广,因为该值占用 64 位,并且为尾数保留了 53 位。它正好是从 -9,007,199,254,740,991 到 9,007,199,254,740,991。然而double
需要两倍的内存。
另一个困难的来源是小数的表示方式。由于小数不能精确表示(0.1f = 0x3dcccccd = 0.10000000149...),浮点数的使用破坏了常见的代数恒等式。
0.1f * 10 != 1.0f
这可能会造成混淆并导致难以检测的错误。一般来说,浮点数不应该使用严格相等。
浮点算术偏离代数正确性的另一个例子:
float x = 16777217.0f;
float y = 16777215.0f;
x -= 1.0f;
y += 1.0f;
if (y > x) {printf("16777215.0 + 1.0 > 16777217.0 - 1.0\n");}
另一个问题是当精确表示的限制被打破时系统的行为。当在整数算术中算术运算的结果大于类型的范围时,可以通过多种方式检测到:处理器标志寄存器中的一个特殊 OVERFLOW 位被翻转,结果与预期明显不同。
如上例所示,在浮点运算中,精度损失会悄无声息地发生。
希望这有助于理解为什么在 C 中需要许多基本的数字类型。
值 1 的浮点数和值 1 的整数是否不会编译为(此处省略大量零)0001
不,float 将被存储为类似于 的东西0x00000803f
,具体取决于精度。
这是什么意思?
O.2
以二进制形式看起来0.00110011001100110011...
会永远继续(并重复)。无论您使用多少位来存储它,它都永远不够。那是因为 5 不能被 整除2
。精确表示它的唯一方法是使用比率来存储它。100000000000000001
并且100000000000000002
很可能四舍五入到相同的数字。您可能还想阅读类似的内容。
结论: