`
jaesonchen
  • 浏览: 311417 次
  • 来自: ...
社区版块
存档分类
最新评论

java二进制运算

 
阅读更多

//对于原码, 反码, 补码而言, 需要注意以下几点:

//(1)  二进制的最高位是符号位, 0表示正数, 1表示负数;

//(2)  正数的原码, 反码, 补码都一样;

//(3)  负数的原码=对应正数的二进制原码,最高位设为1;

//(4)  负数的反码=它的原码符号位不变, 其他位取反;

//(5)  负数的补码=它的反码+1;

//(6)  0的反码, 补码都是0;

//(7)  在计算机运算的时候, 都是以补码的方式来运算的.

 

//原码的得来:(负数的原码,直接把对应正数的最高位改为1),原码能够直观的表示一个负数(能直观的把真值显示出来,-1的原码10000001)。
//补码的设计目的是:使符号位能与有效值部分一起参加运算,从而简化运算规则。使减法运算转换为加法运算。
//java中byte short int long都是有符号整数,char是无符号整数
//有符号整数的最高位为符号位:0表示整数、1表示负数。
//当把二进制转换为十进制时,正整数直接计算结果。 
//负数的十进制结果为:负数的二进制补码(带符号位一起)取反(^) + 1得到无符号二进制表示,计算无符号二进制的十进制结果,然后在结果前加负号(-)。
//十进制负数转化为二进制表示:去除负号,将对应的十进制正数转为无符号二进制数表示,对二进制码取反(^) + 1得到有符号二进制表示。
//byte的表示范围为:-128 to 127,其中负数范围-128 to -1, 整数范围0 to 127,各代表2的n-1次方个数。
//-128的二进制补码为10000000,^(10000000) + 1 = 10000000(无符号二进制),十进制结果为128加负号 = -128。
//-5转换二进制补码:00000101(对应正数的二进制),^(00000101) + 1 = 11111011(负数补码)。
//java中的减法转化为加法:5 - 3 实际执行的是 5 + (-3),00000101 + 11111101 = 00000010 (高位溢出) = 2

//(byte)0x90 -> (byte)00000000000000000000000010010000 -> 10010000(byte负数) -> -(10010000^ + 1) = -(01110000) = -112
System.out.println((byte)0x90);  //-112

 

//Java 使用2 的补码的算术运算,它是非对称的。对于每一种有符号的整数类型(int、long、byte 和short),
//负的数值总是比正的数值多一个,这个多出来的值总是这种类型所能表示的最小数值。
//-(-128) -> -(10000000) -> 10000000^ + 1 -> 10000000 = -128
//-128 - 1 = 10000000 + (-1) = 10000000 + 11111111 = 01111111(高位溢出) = 127
//127 + 1 = 01111111 + 1 = 10000000 = -128
i = Integer.MIN_VALUE;
System.out.println(i != 0 && i == -i);		//true

 

 

//复合赋值操作符包括*=、/=、%=、+=、-=、<<=、>>=、>>>=、&=、^=和|= 
//在运算时自动提升到int,赋值时它们可能会自动地执行窄化原始类型转换。
//死循环:(short)-1 = 0xffff -> 执行符号扩展0xffffffff -> 无符号右移 1位,高位0填充 -> 0x7fffffff -> (short)0x7fffffff -> (short)0xffff = (short)-1

short i = -1;
while (i != 0) {
	i >>>= 1;
}

 

 

//会导致精度丢失的三种拓宽原始类型转换:从int 到float、从long 到float、从long 到double。
//丢失原因:毗邻的浮点数值之间的距离被称为一个ulp,它是“最小单位(unit in the last place)”的首字母缩写词。
//当float和double足够大时,将一个小于最小单位ulp的浮点数加到一个很大的浮点数上时,将不会改变大的浮点数的值。

System.out.println((float)2000000000 ==	(float)2000000050);			//true

 

 

//byte short char 类型在使用int常量赋值时,如果常量值超出类型表示范围需要强制类型转换。否则编译错误。
//编译错误: int常量128超过byte的表示范围,丢失精度出错。   	byte bValue = 128;
//编译错误:int常量2147483648超过int的表示范围,越界出错。  	long lValue = 2147483648;

byte bValue = 127;
long lValue = 2147483648L;

 

//所有的int 变量都是小于或等于Integer.MAX_VALUE 的。因为它被定义为所有int 数值中的最大值。
//int 不能表示所有的整数。无论你在何时使用了一个整数类型,都要意识到其边界条件。
//如果其数值下溢或是上溢了,通常最好是使用一个取值范围更大的类型。(整数类型包括byte、char、short、int 和long。)
//当i 达到Integer.MAX_VALUE,并且再次被执行增量操作时,它就又绕回到了Integer.MIN_VALUE。
//无限循环
for (int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++)
//正确的做法:
for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++)

 

 

//通常最好是在条件表达式中使用类型相同的第二和第三操作数。
//确定条件表达式结果类型的规则:
//如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。
//如果一个操作数的类型是T,T 表示byte、short 或char,而另一个操作数是一个int 类型的常量表达式,
//它的值是可以用类型T 表示的,那么条件表达式的类型就是T。
//否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。

char x = 'X';
int i = 0;
System.out.println(true ? 88 : x);	//X
System.out.println(true ? x : 65535);	//X
System.out.println(true ? x : 65536);	//88
System.out.println(false ? i : x);		//88
System.out.println(true ? x : i);		//88

 

//复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。
//请不要将复合赋值操作符作用于byte、short 或char 类型的变量上。
//如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行	一个窄化原始类型转换。
//复合赋值 E1 op= E2 等价于简单赋值E1 = (T)((E1)op(E2)),其中T 是E1 的类型。

int i1 = 123456;
short s = 0;
s += i1;
System.out.println(s);		//-7616

 

  

//混合类型的计算可能会产生混淆,尤其是十六进制和八进制字面常量无需显式的减号符号就可以表示负的数值。
//为了避免这种窘境,通常最好是避免混合类型的计算。
//负的十进制常数可以很明确地用一个减号符号来标识。但是十六进制和八进制字面常量并不是这么回事,它们可以具有正的以及负的数值。
//如果十六进制和八进制字面常量的最高位被置位了,那么它们就是负数。
System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));	//cafebabe
System.out.println(0x100000000L + 0xcafebabe);                          //3405691582
		
System.out.println(Integer.toHexString((int)(0x100000000L + 0xcafebabe)));  //cafebabe
System.out.println((int)(0x100000000L + 0xcafebabe));                       //-889275714

 

//在long 型字面常量中,一定要用大写的L,千万不要用小写的l。
//要避免使用单独的一个l 字母作为变量名。
//数字1 的水平笔划和垂直笔划之间是一个锐角,而与此相对照的是,小写字母l的是一个直角。
//正确的做法:System.out.println(12345+5432L);
System.out.println(12345+5432l);	//17777

 

//当你在操作很大的数字时,千万要提防溢出——它可是一个缄	默杀手。
//即使用来保存结果的变量已显得足够大,也并不意味着要产生结果的计
//算具有正确的类型。当你拿不准时,就使用long 运算来执行整个计算。

//在运算时使用的是int,在将int结果赋值给long之前,溢出部分被截取,导致结果错误。
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);		//5

//正确的做法: final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;

 

//在需要精确答案的地方,要避免使用float 和double。
//并不是所有的小数都可以用二进制浮点数来精确表示的。
//当一个小数不能用二进制表示时,它被表示成为最接近它的double 值。
//对于货币计算,要放大使用int、long 或BigDecimal。
//一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)。
//后一个构造器将用它的参数的“精确”值来创建一个实例。
		
System.out.println(2.00 - 1.10);	//0.8999999999999999

 

//无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。
//错误的方法,在i为负数时总是返回false
public static boolean isOdd(int i){
	return i % 2 == 1;
}
//正确的方法
public static boolean isOddCorrect(int i){
	return i % 2 != 0;
}

 

//全等关系必须是是自反的、传递的和对称的。
//当比较两个原始类型数值时,操作符 == 首先进行二进制数据类型提升(binary numeric promotion)。
//这会导致这两个数值中有一个会进行拓宽原始类型转换(widening primitive conversion)。
//大部分拓宽原始类型转换是不会有问题的,但有三个值得注意的异常情况:
//将int或long 值转换成float 值,或long 值转换成double 值时,均会导致精度丢失。
//这种精度丢失可以证明 == 操作符的不可传递性。

long x = Long.MAX_VALUE;
double y = (double) Long.MAX_VALUE;
long z = Long.MAX_VALUE - 1;
System.out.print((x == y) + " " + (y == z) + " ");
System.out.println(x == z);		//true true false

 

 

//在两种情况下,插入一对看上去没有影响的括号可能会令合法的Java 程序变得不合法。
//这种奇怪的情况是由于数值的二进制补码的不对称性引起的。
//Java 不支持负的十进制字面常量,int 和long 类型的负数常量都是由正数十进制字面常量前加一元负操作符(-)构成。
//符号-2147483648 构成了一个合	法的Java表达式,它由一元负操作符加上一个int型字面常量2147483648组成。
//通过添加一对括号来注解(很不重要的)赋值顺序,即写成-(2147483648),	就会破坏这条规则。

int i = -2147483648;
//超出范围,编译错误
//int i = -(2147483648);
//long j = -(9223372036854774808L);

 

//千万不要在一个整型字面常量的前面加上一个0;这会使它变成一个八进制字面常量。
//有意识地使用八进制整型字面常量的情况相当少见,你应该对所有的这种特殊用法增加注释。
System.out.println(012);		//10

  

//Math.abs返回return (a < 0) ? -a : a;
//在参数为Integer.MIN_VALUE和Long.MIN_VALUE时,那么它将返回它的参数。
//因为-Integer.MIN_VALUE = Integer.MIN_VALUE
System.out.println(Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE);	//true
		
//不要使用基于减法的比较器,除非你能够确保要比较的数值之间的差永远不会大于Integer.MAX_VALUE。
//要意识到int 的溢出。
Random rnd = new Random();
Integer[ ] arr = new Integer[100];
for (int i = 0; i < arr.length; i++)
	arr[i] = rnd.nextInt();

//错误的做法:会有1/4的运算结果溢出,导致排序混乱
/*
Comparator<Integer> cmp = new Comparator<Integer>() {
	@Override public int compare(Integer i1, Integer i2) {
		return i2 - i1;
	}
};*/
	
//正确的做法
Comparator<Integer> cmp = new Comparator<Integer>() {
	@Override public int compare(Integer i1, Integer i2) {
		 int val1 = i1.intValue();
		 int val2 = i2.intValue();
		 return (val1 < val2 ? -1 : (val1 == val2 ? 0 : 1));
		 //或者不使用等价比较符 return (i1 < i2 ? -1 : (i1 > i2) 1 : 0);
	}
};
Arrays.sort(arr, cmp);

 

 

//在5.0 版中,自动包装(autoboxing)和自动反包装(auto-unboxing)被添加到了Java 语言中。
//(被包装的数字类型有:Byte、Character、Short、Integer、Long、Float 和Double。)
//Java 的判等操作符(==和!=)在作用于对象引用时,执行的是引用ID 的比较,而不是值的比较。这是为了兼容5.0以前版本。
//判等操作符在其两个操作数中只有一个是被包装的数字类型,而另一个是原始类型时,执行的确实是数值比较。这种比较在5.0以前是非法的,所以不会出现不兼容。
//数值比较操作符(>、 >=、 <、 <=):
//当两个操作数都是被包装的数字类型时,数值比较操作符和判等操作符的行为存在着根本的差异:
//数值比较操作符执行的是值比较,而判等操作符执行的是引用标识的比较。

Integer obj1 = new Integer(0);
Integer obj2 = new Integer(0);
System.out.println(obj1 <= obj2 && obj1 >= obj2 && obj1 != obj2);	//true

 

 

分享到:
评论

相关推荐

    java二进制运算器(加、见、乘、除)

    在这个Java二进制运算器中,我们将深入探讨这些基本操作以及如何在代码中实现它们。 1. **二进制数系统**: 二进制是计算机科学中的基础,只有两个数字0和1。所有的计算都在这个系统下进行,因为计算机内部处理的是...

    java二进制运算基础知识点详解

    "Java二进制运算基础知识点详解" Java二进制运算基础知识点详解是Java编程语言中的一些基本概念,它们是理解Java编程语言的基础。下面是对Java二进制运算基础知识点的详细解释。 一、位运算 位运算是Java语言中的...

    CE二进制运算器正式版

    然而,随着Java平台的普及和跨平台优势的凸显,开发者决定将CE二进制运算器移植到Java平台上。Java以其“一次编写,到处运行”的特性,能够运行在不同的操作系统上,这将扩大CE二进制运算器的适用范围,使其用户群体...

    大数运算包含加,减,乘,除,取模,幂运算,模幂运算。支持十进制运算,二进制运算.zip

    支持十进制运算,二进制运算.zip"文件中,我们可以预见到这可能是一个关于大数运算的程序或库,它不仅支持常见的十进制运算,还特别强调了二进制运算。 1. **大数运算**:大数运算通常在需要精确计算或处理大数据量...

    十六进制字符串按位异或运算工具和java位异或运算

    当两个十六进制字符串进行按位异或运算时,会先将它们转换为二进制形式,然后逐位进行异或操作。异或运算是对两个位进行比较,如果两个位相同,则结果为0;如果两个位不同,则结果为1。 在Java中,我们可以利用`...

    java权限设置,和java处理字符和二进制的转换

    Java权限设置和字符、二进制转换 Java权限设置是一种常用的权限控制方法,用于控制用户对系统资源的访问权限。在Java中,我们可以使用位运算来实现权限控制。下面是Java权限设置的知识点: 1. 权限控制算法:我们...

    java基础,进制转换.pdf

    - 二进制数的算术运算:例如,30 - 70的计算过程实际上是将30转换为二进制,再将70的负数也转换为二进制,然后进行二进制的加法运算。 6. 进制转换的计算方法 - 位权表示法:采用2的幂次方表示法来定义不同进制...

    Java把十进制转成二进制(任意进制)的程序

    在Java编程语言中,将十进制数转换为二进制或其他任意进制是一项常见的任务。这主要涉及到数字系统的基础理论以及Java中的字符串处理和数学运算。以下将详细讲解如何实现这一功能。 首先,让我们从最基础的二进制...

    Java实现任意进制的转换

    本文将详细介绍如何使用Java语言实现任意进制之间的转换,并涵盖从二进制到十六进制、从十进制到任意进制以及进行36进制加法的转换方法。 首先,让我们从二进制转十六进制开始。在Java中,我们可以使用`Integer`类...

    java 进制转换,二进制 十进制 十六进制 正数 负数的进制等等!.docx

    CPU通常使用二进制处理数据,而人类更习惯于十进制,程序员尤其是底层开发者则常使用十六进制。八进制在某些旧的计算机系统和编程语言中也有使用,但相对较少。 在Java中,我们可以使用内置的方法进行各种进制之间...

    详谈Java中的二进制及基本的位运算

    Java 中的二进制及基本的位运算 Java 中的二进制是计算技术中广泛采用的数制,使用 0 和 1 两个数码来表示数。Java 中的二进制数据主要是以补码的形式存储的。计算机中的二进制是一个非常微小的开关,用“开”来...

    Java程序十进制转二进制,八进制,十六进制

    在Java编程语言中,将十进制数转换为二进制、八进制和十六进制是常见的操作,尤其在处理计算机数据表示和计算时。本文将深入探讨这些转换方法。 首先,我们要理解不同进制系统的基础。十进制是我们日常生活最常用的...

    汉字十六进制和二进制转换

    通过上述分析可以看出,给定的Java代码示例有效地实现了汉字从二进制到十六进制的转换,以及从十六进制回到二进制的逆过程。这对于理解字符编码、数据表示和转换等概念非常有帮助。同时,这些方法也可以应用于更广泛...

    整数的二进制0-1串片段.java

    根据给定文件的信息,本文将围绕“如何使用Java语言实现将一个整数转换为其对应的二进制0-1字符串”这一主题展开详细讨论,并解析代码中的关键知识点。 ### Java实现整数转二进制字符串 #### 一、背景介绍 在...

    java 实现十六进制数转为图片

    在计算机图形学中,颜色通常由红、绿、蓝(RGB)三个通道组成,每个通道用8位二进制表示,范围从00到FF(十六进制)。因此,一个像素的颜色可以用三组十六进制数表示,例如#FF0000代表红色,#00FF00代表绿色,#0000...

    javascript 二进制运算技巧解析

    1、原码、反码、补码,正数减法转补码加法 js 在进行二进制运算时,使用 32 位二进制整数,由于 js 的整数都是有符号数,最高位0表示正数,1表示负数,因此,js 二进制运算中使用的整数表达范围是 代码如下: -Math....

    统计整数的二进制表示形式中有几个1(java实现)

    统计整数的二进制表示形式中有几个1(java实现),代码中有三种方法,分别是利用除、余的方法,位运算,以及利用“与”运算的方法。其中第三种方法效率最高,二进制数中有几个1,算法中的循环内的运算就执行几次。

    将二进制转换为16进制

    例如,Java中的`Integer.toHexString`方法可以将整型二进制数值转换为十六进制字符串。如果你看到的文件名为"HomeWork_3_4.java",这可能是一个学生编程作业,其中实现了将二进制转换为十六进制的算法。在这个程序中...

Global site tag (gtag.js) - Google Analytics