高清免费观看电视网站,如何把自己做的网站,wordpress报错,wordpress透明化插件计算机里面关于数值的处理自有一套体系理论#xff0c;与现实生活中我们所习惯使用的不太一样。如果对其不了解#xff0c;在使用计算机的过程中便可能发生一些意想不到的错误。 今天本文就来简明地介绍计算机里面的数值方面的一些知识#xff0c;并用具体例子来说明可能出现…计算机里面关于数值的处理自有一套体系理论与现实生活中我们所习惯使用的不太一样。如果对其不了解在使用计算机的过程中便可能发生一些意想不到的错误。 今天本文就来简明地介绍计算机里面的数值方面的一些知识并用具体例子来说明可能出现的一些问题。
一、一些概念
机器数数值在计算机内部的编码也就是实际存储的 0/1 序列。
真值机器数想要表示的实际数值可理解为现实生活中我们平常所用的有正负号的数。
机器数与真值的对应关系就是数值在计算机内部的编码主要有 4 种原码反码补码移码。
原码 原码由 1 位符号位和数值部分组成数值部分就是真值的绝对值。
特点
与真值的对应关系直观0 的表示不唯一有 0(00…000) 和 -0(10…000)。加减法复杂需要判断符号
反码 反码由 1 位符号位和数值部分组成。表示一个正数时同原码表示一个负数时符号位不变数值部分各位求反。
反码相对于原码并没有多大改进提升现已不用来表示数据。
补码 补码也是由 1 位符号位和数值部分组成。表示一个整数时同原码表示一个负数时符号位不变数值部分各位求反末位加 1。
原码到补码快速转换(负数)符号位不变从右到左的第一个1不变其他位取反。 补码到原码(负数)转换方法同上符号位不变数值部分各位求反末位加 1。也就是说符号位不变从右到左的第一个1不变其他位取反。
原因按理说补码到原码应为原码到补码的逆操作即减 1 再求反。设补码为A则原码为 ~(A - 1)即11…111 - ( A - 1) 11…111 - A 1也就是~A 1。 因此 ~A 1 ~(A - 1)
一个数的补码取负连同符号位各位求反末位加1即从右到左的第一个1不变其他位取反。
例如 4 位补码[x]补 1011[-x]补 0101
特点 0有唯一表示00…000所以补码能表示的数范围比原码反码多1表示范围为 -2n-1 ~ 2n-1-1 加减法符号位能直接参与运算。 现代的计算机整数基本都采用补码表示。
移码
移码主要用于浮点数的阶码部分后面会讲浮点数的阶有正负两个浮点数比较时需要比较阶码来対阶。为方便比较将阶加上一个偏置常数使其变成正数因为加的都是同一个偏置常数阶的差值也是不会改变的。
所以移码就是数值本身加上一个偏置常数得到偏置常数一般是2n-1或者2n-1-1。
上述都是定点数的表示方法定点数顾名思义小数点是约定不动在一个固定位置的。定点数分为定点小数和定点整数。 定点整数的小数点固定在数的最右边一般用来表示整数。 定点小数的小数点固定在数的左边一般表示浮点数的尾数部分。
而浮点数的表示类似于科学计数法它的指数部分可以变动相应的尾数部分也跟着变化就像小数点在浮动一样所以叫做浮点数浮点数后面再详解。
二、数值比较
整数分为无符号整数和有符号整数**给定一个数在计算机里如何存储表示成 0/1 序列是编码的事而对这 0/1 序列如何解释是上层软件的事情。**如c语言中可解释为有符号数和无符号数而java中只解释为有符号数。
数值比较时得确定类型才能比较。通常默认为有符号数相比若出现无符号数则按照无符号数相比。
概念模糊抽象看下面的经典例子来理解
第一个两数按照有符号数相比11…111B(-1) 00…000B(0) 第二个0U为无符号数所以两数按照无符号数相比-1的机器数为11…111按照无符号数解释为232 -1这时是大于0的。11…111(232-1) 00…000(0)
注括号前的二进制表示为数值的机器数通常由补码表示括号里面的是c语言对机器数 按照无符号数或者有符号数 解释出来的数。
看第二个例子前补充一些知识
判断一个常量类型时是先将符号“踢开”只看后面的数值在哪个区间根据区间确定类型。
c90 和 C99 是c语言的两套标准两套标准对常量的处理不同下面的例子以 C90 说明
第一个2147483647为 int 型两边按照有符号数相比01…111B(231 - 1) 10…000B(-231)
第二个2147483647为 int 型前者带有U表示无符号数按照无符号数相比01…111B(231 - 1) 10…000B(231)
第三个2147483648为 unsigned int 型但被强制类型转换为int型所以按照有符号数相比01…111B(2^31 -1^) 10…000B(-231)
第四个2147483648为 unsigned int 型两数按照无符号数相比01…111B(231 -1) 10…000B(231)
完全理解上述几个例子后对数值比较这一块应该没问题了在此再强调总结一番。
一个数据怎么存储成 0/1 序列是编码的事怎么解释这个 0/1 序列是上层软件的事。
也就是说上述的数值比较中 2147483648 的机器数始终是10…000B2147483647的机器数始终是01…111B之所以出现不同的比较结果是因为 c 语言对它们进行了不同的解释处理。
有了上面的基础再来看几个程序 打印结果如上图注释原因如下
x 的机器数为 11…111u 的机器数为 10…0000 所以x按照无符号数解释为232 - 1 4294967295按照有符号数解释为 -1。 u按照无符号数解释为 231按照有符号数解释为 -231
由上也可以看出机器数为10…000的数是能表示的最小整数取负后溢出还是它本身。 这是计算数组元素和的一个函数按照程序所设想length 传入 0 时应该返回 0但实际上并非如此。这个程序理论上会无限循环实际运行时会发生数组越界导致异常。
理论上无限循环的原因len 为无符号数传入0时len - 1 为 -1机器数为11…111按无符号数解释为232 -1是 32 位能表示的最大整数。
前面说过有无符号数参与比较时两边都按照无符号数相比所以不管 i 怎么变化始终小于等于右边那个最大的值。当 i 232 -1 时下一步又会回绕到0
但实际运行时肯定不会有那么长的数组所以会发生越界错误。 此函数时用来比较两个字符串str1, str2谁长用到了库函数strlen()其原型如上。此函数设想 str1 长时返回 1否则返回 0但实际上只有 str1 和 str2 长度相等时会返回 0其他时候都返回1
问题就出在函数strlen()的返回值是size_t即unsigned int。也就是说比较是按照无符号数来比较的无符号数永远是大于等于 0 的所以只有两个串儿长度相等时会使左边式子等于 0其他时候左边结果的机器数中肯定有非 0 位那么按无符号数解释就会大于0也就返回1了。
改进方法返回语句改成return strlen(str1) strlen(str2)直接让两个串的长度比较而不是做减法再与0比较。
这个例子说明调用库函数也要小心对其的返回类型参数类型要有了解否则可能就会出错。
三、浮点数(IEEE 754) IEEE 754浮点数标准规定的浮点数格式如上图所示简要解释如下 符号位1表负0表正 阶码用移码表示是一个定点整数偏置常数是2n - 1所以单精度的偏置常数位127双精度的偏置常数位1023 尾数是一个定点小数实际表示24位有效数字隐含了一位 1 实际尾数为1.*****B
表示范围
32位单精度 负数的表示只不过是符号位发生变化所以32位单精度浮点数能表示的极值如下 最小正数2-126 最大整数(2 - 2-23) * 2127 最大负数-2-126 最小负数-(2 - 2-23) * 2127
64位双精度
64位双精度浮点数原理同上能表示的极值如下 最小正数2-1022 最大整数(2 - 2-52) * 21023 最大负数-2-1022 最小负数-(2 - 2-52) * 21023
所以IEEE 754 浮点数能表示的范围如下面数轴图所示 浮点数每个 2n-1~2n 区间内能表示的数的个数都是223个(尾数23位)而且等距。比如 1~2 之间有 223 个数2~4 之间也有 2^23 ^个等距的数所以浮点数表示的密度并不相等距离0越近密度越大。
也就是说并不是每个小数都能精确表示当输入一个不可表示的数时机器会将其转换为最近能表示的数。这也是为什么编写程序时不要用浮点数来进行比较特别是相等的情况因为你想比较的数可能无法表示机器自动给你转换了。
特殊表示
阶码全0尾数全0
0 或者 -0正负看符号位
阶码全1尾数全0
∞-∞正负看符号位
阶码全1尾数非0
阶码全1尾数非0NaN(Not a Number)不是一个数0 / 0, 0 * ∞ 等会产生NaN。
阶码全0尾数非0
从上述的数轴图中可以看出-2-126 ~ 2-126之间的数是表示不了的引进非规格化数可解决这问题。非规格化数的尾数中的隐含位为0阶码虽然全0但阶还是-126。如此便在-2-126~2-126之间添加了2 * 223个数解决了下溢问题。
同样有了上述的基础知识来看一些例子
这几个题都很简单注意几点就行
精度大的转换成精度小的可能会出问题精度小的转换成精度大的不会有问题。浮点数取负直接变符号位就可。
最后一个是浮点数的加减运算问题后面详解在这可以举个例子简单理解下当 d 很大 f 很小时d f 得到的结果就等于 d 舍去了f所以左边等于 0 右边等于 f两边不等。
四、数值运算
按位运算和逻辑运算
这两种运算比较简单只是要区分一下概念。
按位运算恰如其名是对数值的位进行与或非运算。
逻辑运算的操作数只有 true 和 false对数值的处理为非零即真。
左移右移
移位分为逻辑移位和算数移位 逻辑移位不考虑符号位左移时高位移出低位补0右移时低位移出高位补0 算术移位考虑符号位左移时高位移出低位补0右移时低位移出高位补符号位
c语言中编译器进行移位运算和CPU进行移位运算是不一样的 编译器进行实际移位比如移动w位实际也移动w位 CPU移动 w % k w为所移位数k为数据类型的位数
看下面程序帮助理解打印结果已注释在后面
位扩展位截断
扩展分为 0 扩展和符号扩展数据转换时短数向长数转换时需要位扩展。
0扩展用于无符号数在原来的数前面添足够的0即可。
符号扩展用于有符号数在原来的数前面添足够的符号位即可。
位截断长数向短数转化时会发生截断规则比较粗暴简单直接“砍掉”高位留下低位即可。
长数的表示范围肯定大于短数所以截断一个数可能会改变原来的值。看下面一个例子
经过截断再扩展后 32768 变成了 -32768所以再截断时要注意溢出问题。 计算机里整数浮点数的加减乘除运算的实际过程都很复杂内容很多建议直接看唐朔风的计算机组成原理第六章数字逻辑相关书籍中加法器乘法器等的电路实现。深入理解计算机系统对各种数值算法的理论推导。公众号内回复电子书即可获得相应书籍。下面就只说说其中我认为比较重要需要的一些东西。
加减运算
现代计算机里面整数都可以看做是用补码表示的统一了加减法符号位也能和数值部分一起进行计算。
不论是无符号数还是有符号数都先按照实际的机器数做运算得到的结果再解释成相应的无符号数或有符号数。
整个计算机的运算系统都是采用模运算得出的结果如果超出计算机表示的位数会直接丢掉高位。
例如若两个数为a, b对应的机器数为A, B则a b 相应操作为 A Ba - b 为A ~ B 1。再对计算得到机器数解释成最终结果。
乘除运算
乘法主要需要考虑溢出问题n 位数乘 n 位数结果可能需要2n位来表示。c语言中截直接断成 n 位来实现(来自深入理解计算机系统)但经过试验结果似乎会自动扩展。
只要结果在你机器的表示范围内就会自动扩展比如两个 short 型的数相乘结果会自动扩展成 32 位来正确表示结果除非限定结果还是个 short 型才会截断。
整数除法一般没有溢出问题因为商的绝对值不会大于被除数的绝对值。
除了一种情况有符号数中最小数除以-1例如 int 型的情况-2147483648 / -1 便会溢出结果还是 -2147483648 本身。
在这说点 c 里面一些有趣的东西可以算是bug吧。上述说的有符号数中的最小数即机器数为10…000的数(设为s)取负后还是它本身这是没问题的机器数也的确是相同的。所以按理说 s -s。这在32位int型64位long long的情况下成立但是在short情况下不成立。所以 c 里面关于数值的东西有许多奇奇怪怪的东西诸位感兴趣可以去尝试。
常量乘除
乘除法运算所花的时间远远多于移位加减运算的时间因此编译器处理变量与常量乘除时会以移位加法减法的组合运算来代替乘除法。
来看一个具体例题x为一整形变量现要计算55 * x给出一种计算表达式使得所用的时钟周期最少。
题目很简单主要是想说明怎么转换。
乘以 2n 相当于左移 n 位除以 2n 相当于右移 n 位。 **左移需要注意高位的溢出问题而右移则需要注意舍入问题。一般的舍入规则是向0舍入但用移位来实现除法是向下舍入的。**对于正数来说没什么问题向下舍入就是向0舍入。但是负数就有问题了向下舍入并不是向0舍入需要校正。
为什么移位来实现除法是向下舍入的呢正数应该很好理解右移之后丢掉移出的小数部分数值自然变小了。如果是负数右移之后丢掉小数部分数值不应该变大吗但别忘了负数在计算机里由补码表示它跟真值对应的关系是要求反的所以换成真值后会发现数值依然是变小的。
来看一个例子理解向下舍入应该会更清楚
如何校正呢 既然负数也是向下舍入那么在它移位之前先给它加上一个偏移量让它变大点那么移位后舍入不就正确了。
如果右移n位这个偏移量就是2n-1原来是 x / 2n现在是(x 2n - 1) / 2n x / 2n (2n-1 / 2n)相当于加了一个极限小数来校正。
这个数可以在十进制下来理解比如移动一位也就是一位小数的情况下-2.1-2.9都要舍入到-2应该怎么操作呢将两个数都加上一个0.9就行了这里0.9就是十进制一位情况下的一个极限小数换成二进制同理二进制 n 位的一个极限小数就是2n-1 。
浮点数运算
加减法
1、対阶
只有阶数相等尾数才能直接相加减。 対阶原则小阶对齐大阶阶小的尾数右移右移尾数为阶差。
注: 右移时注意隐含位 1 也要一起参与移位为保证精度低位移出的位不要丢掉后续参与尾数加减。
2、尾数加减
尾数是由定点原码小数表示这里没有符号位所以加减就是普通的二进制加减法。这里注意隐含位和対阶时移出的附加位也要参与运算。
3、规格化
上述尾数加减后得到结果可能五花八门千奇百怪需要规格化变成 IEEE 754 的标准形式。**简单说来就是把尾数变成1. **B的形式然后相应的调整阶码。小数点的位置与阶码息息相关小数点浮动了阶码当然也要相应变化。
4、结果舍入
対阶和尾数规格化的时都可能右移为保证精度会将移出的位保留下来参与中间运算得出最终结果后再舍入。IEEE 754规定至少保留 2 位紧跟在尾数右边的叫做保护位(guard)保护位右边的叫做舍入位(round)为提高精度舍入位右边还有一位粘位(sticky)。只要粘位右边有任何的非0数就置1否则置0。
5、阶码溢出判断
结果的阶码全 1 表上溢产生异常或者结果置为∞。 结果的阶码全 0 表下溢产生异常或者结果置0
这就能解释前面为什么 (d f ) - d 不一定等于 f d 如果很大f 很小対阶时f 看齐d尾数可能一直右移导致有效位没有了变成了全0再进行尾数加减时 d 值不变。所以左边等于0右边等于 f两者不等。
乘除法
浮点数乘法运算过程类似加减法主要区别在于乘除法不用対阶其他过程基本一样。也好理解就像我们平时用科学计数法做算数时加减法那肯定是需要指数相等尾数才能相加减而乘除法时可以直接尾数与尾数运算指数与指数运算一个道理。 计算机里面有关数值的问题有很多不仅计算机本身有一套规则各语言的编译器也有自己的规则里面的弯弯绕绕很多会造成各种奇奇怪怪的问题。
本文也只是捡重点做简要介绍系统的理解还是去看书吧这里推荐深入理解计算机系统里面数值的基础理论很系统唐朔风的计算机组成原理对运算这一块而讲的比较详细袁春风老师的计算机系统基础也很经典比较薄容易读下去。本文很多地方的例子就是采用她书上或者PPT中的例子。然后再看看数字逻辑的相关书籍了解了解加法器乘法器等的电路实现来加深理解。
好啦本文就到这里如果哪有错或不足还请大家批评指正
喜欢本文的朋友还请点个赞支持一下关注公众号Rand_cs可获取更多关于系统的精品文章还有关于计算机的各类电子书总能找到你需要的赶快关注吧