原码:
一个正数,转换为二进制位就是这个正数的原码。负数的绝对值转换成二进制位然后在高位补1就是这个负数的原码。
但是原码有几个缺点,零分两种 +0 和 -0 。很奇怪是吧!还有,在进行不同符号的加法运算或者同符号的减法运算的时候,不能直接判断出结果的正负。你需要将两个值的绝对值进行比较,然后进行加减操作 ,最后符号位由绝对值大的决定。于是反码就产生了。
反码:
正数的反码就是原码,负数的反码等于原码除符号位以外所有的位取反。
解决了加减运算的问题,但还是有正负零之分,然后就到补码了
补码:
正数的补码与原码相同,负数的补码为 其原码除符号位外所有位取反(得到反码了),然后最低位加1.
---------------------------------------------------------
1.计算机中加法运算比减法运算实现起来简单很多,对反码做减法运算可以将被减数看作 加上被减数的负数形式,也就是将被减数出符号为取反。
2.在原码中,最高位是符号位。那么就存在正数0 -> 0000 0000 0000 0000,还存在负数0-> 1000 0000 0000 0000,这就存在两种0,反码也存在这个问题。 而在补码中,正数0 -> 0000 0000 0000 0000,假设存在负数0,那么按照补码的转换规则为 -> 1111 1111 1111 1111。可以看出这个数表示-2的15次方,不是-0,所以补码只有一个0。
-------------------------------------
经过验证为-2147483648 阶乘的最后存储不了变成负数了,是因为32bit存贮不了溢出了,应该说是往前移了,也就是把最开始的一个bit顶出去了。
----------------------------
浮点数结构
由此可以看出,在计算机中表示一个浮点数,其结构如下:
尾数部分(定点小数) 阶码部分(定点整数)
阶符± 阶码e 数符± 尾数m
这种设计可以在某个固定长度的存储空间内表示定点数无法表示的更大范围的数。
科学记数法以底数为?2?的小数来表示浮点数。32?位浮点数用?1?位表示数字的符号,用?8?位来表示指数,用?23?位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数?2?)小数来表示。对于64?位双精度浮点数,用?1?位表示数字的符号,用?11?位表示指数,52?位表示尾数。
-----------
最根本的原因,有些小数无法用二进制精确表示,比如0.1用二进制表示时,产生了循环,由于内存有限,只能近似存储0.1,所以内存中的0.1是0.1的近似值
先以一个题目来开头:
下列表达式中,可以得到精确结果的是(B)。
A. double d1 = 3.0 - 2.6;
B. double d4 = 2.5 * 1.5;
C. double d2 = 30/300;
D. double d3 = 1/2 + 0.5;
这个题目只有B能获得最终精确的结果是为什么呢?
这要观察小数在计算机里面的存储形式了
因为数据在计算机中存储的方法都是以二进制形式存储
由二进制的排列组合来得到不同的数据
那么小数也会使用二进制来存储。使用*2的方式来不断提取整数部分
A 3.0 的二进制形式 因为小数为0 所以只有整数部分011
2.6分为两部分,2的二进制为010
0.6的二进制为
0.6*2=1.2 取1
0.2*2=0.4 取0
0.4*2=0.8 取0
0.8*2=1.6 取1
所以会无限循环下去,错~ 因为double也是有取值范围的,所以会取一个近似值
依次类推,只有B可以得到。
所以当银行或者金融超市等地方需要精确值时,使用浮点数保存数据会出现问题。所以java提供了解决办法BigInteger和BigDecimal
BigDecimal使用方法将基本数据类型变成BigDecimal类型,然后使用增加乘除的方法来进行计算,提供了一系列运算方法
由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。
因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数
---------------------
本文来自 小笨笨大蛋蛋 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u010419438/article/details/39126809?utm_source=copy