JAVA——数值类型的运算方式总结

       Java的运算符有赋值运算符、算术运算符、比较运算符、逻辑运算符、位运算符、复合赋值运算符。今天主要学习下位运算和位运算的常见使用场景,整数数值类型运算时类型溢出、浮点数值类型运算时精度丢失,导致运算结果异常的原因和解决办法。

位运算

        位运算是按照整数的二进制位进行移位、与、或、取反、异或的运算。除按位与和按位或运算符外,其他只能用于处理整数的操作。整数数据在内存中以二进制的形式进行表示,如7的二进制表示为00000000 00000000 00000000 00000111共32位,左边最高位的是符号位,最高位是0则表示正数,若为1则表示负数,负数采用补码表示。

         正数:原码=反码=补码

         反码:原码中除最高位(符号位)以外每一位取反;

         补码:反码 + 1;


按位与  & 

         按位与为双目运算符,符号为 &。运算规则:两个整型数据的二进制对应位都为1则结果对应位为1否则为0。若两个数精度不同则结果与精度高的数相同。

public class b01 {      //有0为0,无0为1
	public static void main(String[] args) {
		int x = 35;          //100011
		int y = 43;          //101011
		int t = x & y;       //100011
		System.out.println(t);//35
	}
}

按位或  |

         按位或为双目运算符,符号为 |。运算规则:两个整型数据的二进制对应位都为0则结果对应位为0否则为1。若两个数精度不同则结果与精度高的数相同。

public class b01 {      //有1为1,无1为0
    public static void main(String[] args) {
		int x = 35;          //100011
		int y = 43;          //101011
		int t = x | y;       //101011
		System.out.println(t);//43
	}
}

按位异或   ^

         按位异或为双目运算符,符号为 ^。运算规则:两个整型数据的二进制对应位都相同则结果对应位为0否则为1。若两个数精度不同则结果与精度高的数相同。

public class b01 {       //相同为0,不同为1
	public static void main(String[] args) {
		int x = 35;          //100011
		int y = 43;          //101011
		int t = x ^ y;       //001000
		System.out.println(t);//8
	}
}

按位取反(非) ~

        按位非为单目运算符,符号为  ~。运算规则:当前操作整数的二进制表示0 —>1,1 —>0。若两个数精度不同则结果与精度高的数相同。

public class b01 {          //0和1 互换
	public static void main(String[] args) {
		int x = 167776589;   //00001010 00000000 00010001 01001101
		int t = ~x;          //11110101 11111111 11101110 10110010
		System.out.println(t);//-167776590
	}
}

左移  <<

        运算规则:将运算符左边的操作数的二进制,按照运算符右边操作数指定的位数向左移动,右边移空的部分补0。在数值计算中相当于  操作数\times 2^{位移数} 。

public class b01 {          
	public static void main(String[] args) {
		int x = 6;     // 00000110 = 6
		int y = x << 1;// 00001100 = 12
		System.out.println(y);//12
		int t = x << 3;// 00110000 = 48
		System.out.println(t);//48
	}
}

带符号右移  >>

        运算规则:将运算符左边的操作数的二进制,按照运算符右边操作数指定的位数向右移动,如果最高位是0,右移空的位就填入0,如果最高位是1右移空的位置填入1(原码最高位为符号位不参与右移)。在数值计算中相当于操作数\setminus 2^{位移数}

public static void main(String[] args) {   
		//负数右移时
		//原码->反码->补码->右移->反码->原码
		//右移时:符号位不动,右移补1
		int x = -24;    // 10000000 00000000 00000000 00011000 = -24
		int y = x >> 1;// 10000000 00000000 00000000 000001100 = -12
		System.out.println(y);//-12
		int t = x >> 3;// 10000000 00000000 00000000 000000011 = -3
		System.out.println(t);//-3
	}
}

无符号右移  >>>

        运算规则:将运算符左边的操作数的二进制,按照运算符右边操作数指定的位数向右,(无论最高位是1还是0)左边被移空的部分都补0。在数值计算中相当于操作数\setminus 2^{位移数}

public class b01 {   //不管最高位是1还是0
	public static void main(String[] args) {   
		int x = 24;     // 00000000 00000000 00000000 00011000 = 24
		int n = x >>> 1;// 00000000 00000000 00000000 000001100 = 12
		System.out.println(n);//14
		int y = -24;    // 10000000 00000000 00000000 00011000 = -24
		int m = y >>> 1;// 01000000 00000000 00000000 00001100 = 2147483636
		System.out.println(m);//2147483636
	}
}

常见用途

        与运算:判断奇偶(a&1 == 0 a为偶数,a&1 == 1 a为奇数)

        异或运算:交换两个整数(a=a^b,b=a^b,a=a^b)

        配合使用:求平均值,防止溢出( (x&y) +((x^y)>>1) )

        移位运算:  右移:计算指定数值n的50%(n>>1)

                            左移:计算指定数值n的2倍(n<<1)

位运算的优劣

优势

         运行效率更高:  直接对整数的二进制表示进行操作,在硬件层面直接执行

         资源消耗更小:  需要资源较少,不需要像算术运算那样复杂的硬件支持

         实现特定任务:  实现一些任务的最优选,如位掩码,权限控制,状态压缩,加密算法。

         提供并行处理:  可以很容易的在多个位上同时进行,处理大型数据时提供并行处理的潜力。

缺点

        可读性较差: 通常不如传统运算直观,代码的可读性较差

        有易错倾向:容易出错,尤其在处理边界情况或进行不熟悉的操作时

        适用范围小:只适用于整数类型(整形int,长整型long,短整型short,字节型byte)和字符型char

        溢出结果异常:不检查溢出,故在一些情况下会导致结果异常

        算法复杂:   对于复杂的数据结构和算法,位运算可能不是最佳选择,可能需要更复杂的逻辑

数值运算结果异常,原因及解决办法


整形类型溢出

         整形:byte(8位,1字节,-128~127),short(16位,2字节,-32768~32767)

                    int(32位,4字节,-2147483648~2147483647),

                    float(64位,8字节,-2^63~2^(63-1) )

原因

       由于整数存在范围限制,如果计算结果超出了范围限制,就会产生溢出,而溢出不会出错,会得到一个奇怪的结果(这是因为二进制最高位是符号位表示正负,若超过范围,最高位诶的符号位就会发生变化,由正数变为负数)

       如:2147483640+15(int类型最大是2147483647)

public class b01 {
	public static void main(String[] args) {
		int x = 2147483640;
		int y = 15;
		int sum = x + y;
		System.out.println(sum);//-2147483641
	}
}

           其运算过程如下:

       

解决方法

       可以将int换成long,因为long可以表示的范围更大(-2^63——2^(63-1) ),所以类型不会溢出。(如果有更高的要求,long也不能满足,可以使用引用数据类型BigInteger)

public class b01 {
	public static void main(String[] args) {
		long x = 2147483640;
		long y = 15;
		long sum = x + y;
		System.out.println(sum);//2147483655
		
	}
}

浮点型精度丢失  

         两个基本的浮点数类型:float(单精度),double(双精度)。

                                                float(32位,4字节,有效小数6~7位)

                                                double(64位,8字节,有效小数15~17位)

原因

        Java中浮点数的实现采用了IEEE 754标准,该标准使用二进制表示浮点数,由于二进制的局限性,通常无法精确表示所有小数(小数转二进制时对其进行乘二取整),这导致浮点数在某些情况下会出现舍入误差,也就是精度丢失问题。如:

0.9*2=1.8//取整数1
0.8*2=1.6//取整数1
0.6*2=1.2//取整数1
0.2*2=0.4//取整数0
0.4*2=0.8//取整数0
0.8*2=1.6//取整数1
......//开始循环,永远无法消除小数部分
二进制表示为:111001......
public class b01 {
	public static void main(String[] args) {
		double x = 10 - 9.9;
		double y = 1.0 - 9.0 / 10;
		//观察x,y是否相等
		System.out.println(x);//0.09999999999999964
		System.out.println(y);//0.00999999999999998
		//正确答案应是0.1
	}
}
解决方法

    方法一:使用BigDecimal类

           Java给我们提供了BigDecimal类,该类用于对超过16位有效位得数进行精确的计算,可以用于精确地表示和计算十进制数。通过使用BigDecimal类的String类型的构造函数来初始化BigDecimal对象,我们可以避免传统的浮点数表示方式带来的精度丢失。通过调用.subtract()方法,我们可以对这两个BigDecimal对象进行减法运算,而不会发生精度丢失。

public class b01 {   
	public static void main(String[] args) {   
		BigDecimal x = new BigDecimal("10");//使用String类型的构造参数
		BigDecimal y = new BigDecimal("9.9");
		BigDecimal sum = x.subtract(y);//计算 10 - 9.9
		System.out.println(sum);//输出结果0.1
	}
}


方法二:四舍五入
       在Java中,我们可以使用BigDecimal类的setScale()方法来设置小数位数,并使用RoundingMode类的枚举常量来指定四舍五入的方式。

import java.math.BigDecimal;
import java.math.RoundingMode;

public class b01 {   
	public static void main(String[] args) {   
		BigDecimal x = new BigDecimal(10);//这里可以不用String类型的构造参数
		BigDecimal y = new BigDecimal(9.9);
		//.setScale()设置小数位数为2
		//RoundingMode.HALF_UP指定四舍五入的方式
		BigDecimal n = x.subtract(y).setScale(2, RoundingMode.HALF_UP);//在计算结果上调用setScale()方法
		System.out.println("Sum: " + n);//0.10
	}
}

        在我们对精度的要求不高时,计算量比较大时,double和float仍是我们的最佳选择,它的计算效率相较于BigDecimal要快得多,所以我们可以提升算法来规避进度丢失的问题。涉及精确计算时建议选择BigDecimal来提高计算结果的准确性。

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值