zoukankan      html  css  js  c++  java
  • JAVA的枚举基本操作,对原码反码补码的理解及为运算的深入理解,浮点数计算的误差分析

    深入浅出的了解枚举类型

      先看一段代码:

    enum Size{SMALL,MEDIUM,LARGE};
    public class EnumTest {
    
        public static void main(String[] args) {
            Size s=Size.SMALL;
            Size t=Size.LARGE;
            //s和t引用同一个对象?
            System.out.println(s==t);  //false
            //是原始数据类型吗?
            System.out.println(s.getClass().isPrimitive());//false
            //从字符串中转换
            Size u=Size.valueOf("SMALL");
            System.out.println(s==u);  //true
            //列出它的所有值
            for(Size value:Size.values()){
                System.out.println(value);//顺序输出枚举类的值
            }
        }
    
    }

    首先我们定义了一个枚举类Size,在主函数中定义了两个对象,分别赋值为SMALL和LARGE

    首先判断的是他们是否为同一个对象?结果:FALSE

    第二个输出的是他是原始的数据类型吗?结果:FALSE

    第三个通过将一个普通的字符串转换成一个枚举对象,然后在判断他是否与初始定义的枚举对象相同?结果:TRUE

    第四个通过一个foreach语句,和枚举的一个方法values()实现将枚举类型成员以数组的形式返回,然后按顺序输出枚举成员的值。

    通过以上的程序事例,让我们了解到,枚举类型属于引用类型,每个具体的值都引用特定的对象,相同的值引用的是同一个对象,就像s==u一样。

    枚举类型还有个方法:ordinal()用来得到枚举成员的索引,例如上个代码中的:System.out.println(t.ordinal());结果为:2

    还可以比较两个枚举对象在定义时的顺序:compareTo(),例如:Size q=Size.MEDIUM;  System.out.println(q.compareTo(t));  结果为:-1,因为q对象的值要小于t对象的值,用它两下标相减。

    除此之外:还可以在枚举类型中添加新的方法

    enum Color {  
        RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
        // 成员变量  
        private String name;  
        private int index;  
        // 构造方法  
        private Color(String name, int index) {  
            this.name = name;  
            this.index = index;  
        }  
        // 寻找下标对应的名字  
        public static String getName(int index) {  
            for (Color c : Color.values()) {  
                if (c.getIndex() == index) {  
                    return c.name;  
                }  
            }  
            return null;  
        }  
        // get set 方法  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public int getIndex() {  
            return index;  
        }  
        public void setIndex(int index) {  
            this.index = index;  
        }  
    }  
    public class Enum {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Color a=Color.RED;
            System.out.println(Color.getName(2));
            System.out.println(a.getName()+" "+a.getIndex());
            
        }
    
    }

    在枚举类型中添加私有数据:index和name还添加了寻找下标所对应的名字的方法,在主函数中调用枚举类,实现新方法。

    输出为:

    绿色
    红色 1

    原码,反码,补码

    原码:是最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。

    举个简单的栗子:

    0101,其最高位为‘0’,证明他首先是正数,其次再用二进制的方法来计算它  sum=1*22+0*21+1*20=6,因此该二进制数代表的就是6

    1111,其最高位为‘1’,证明他首先是负数,其次再用同样的方式来计算  sum=-(1*22+1*21+1*20)=-7,因此该二进制数代表的就是-7

    该表可以帮助你进一步的理解原码

    反码:正数的反码还是等于原码,负数的反码就是他的原码除符号位外,其他位取反(例如:如果原先是1->0,原先是0->1)

    例如:-5所对应的二进制数是:1101  他的反码就是:1010

    给一些数的反码来进行观察

    补码:正数的补码等于他的原码,负数的补码等于他的反码+1,符号位不变

    例如:-5的二进制数是:1101  反码:1010  补码:1011

    说完基本的概念后,我们来看一下一些数,位运算是如何计算的:按位与&,按位或|,按位取反~,按位异或^

      按位&:如果两个数的对应位都为1,结果才为1.

    public class ma {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int a=4,b=5;
            System.out.println(a&b);
        }
    
    }

    猜一猜结果为多少:4.

    如果两个数为一正一负呢?

    public class ma {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int a=2,b=-5;
            System.out.println(a&b);
        }
    
    }

    你的答案是不是:0,恭喜你答错了,实际输出的是:2.

    为什么呢?因为在计算机中正数的原码,反码,补码都是一样的,而负数的却是不一样的,为了能够实现减法的运算,在JAVA中所有的数都是用补码来存储的。

    不信,咱们可以算算。

    2  补码:0010

    -5  原码:1101  反码:1010  补码:1011

    0010&1011=0010  该结果为2,答案正确。

      按位或:两个操作数中对应位都是0,结果才是0,否则为1.

    public class ma {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int a=2,b=-5;
            System.out.println(a|b);
        }
    
    }

    大家想一想这个数是多少?

    答案是:-5

    2  补码:0010

    -5  原码:1101  反码:1010  补码:1011

    0010|1011=1011  1011看符号位是‘1’,证明他是个负数,而负数都是以补码的形式来存放的,因此,我们要给他还原回去

    1011  ->1010  ->1101  结果为:-5  答案正确

      按位取反:~就是将操作数二进制的1修改为0,0修改为1

      按位异或:^当两个操作数的二进制位表示相同时为0,否则为1

    ③字符串连接的小常识

    public class Test {
        
    public static void main(String[] args) {
            int X=100;
        int Y=200;
        System.out.println("X+Y="+X+Y);
        System.out.println(X+Y+"=X+Y");
        }
    }

    输出:

    X+Y=100200
    300=X+Y

    字符串与变量相+时,就会形成一个新的字符串

    先进行变量的相+时,则会先进行数值相加,然后再连接字符串  

    ④同名变量的屏蔽原则

      每个变量都有一个“有效的”区域,出了该区域,变量就不再有效。

    例如:

    package com.大数计算;
    
    public class xiaoce {
    static int a=10;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int a=5;
            System.out.println(a);
        }
    
    }

    输出:5

    package com.大数计算;
    
    public class xiaoce {
    static int a=10;
    static void f()
    {
        int a=0;
        System.out.println(a);
    }
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int a=5;
            f();
            System.out.println(a);
        }
    
    }

    输出:0  5

    在f()方法里,a的有效范围就是f()方法,故输出0

    ⑤double类型的运算精确吗?

    package com.大数计算;
    
    public class TestDouble {
    
        public static void main(String args[]) {
            System.out.println("0.05 + 0.01 = " + (0.05 + 0.01));//尾数不一样
            System.out.println("1.0 - 0.42 = " + (1.0 - 0.42));
            System.out.println("4.015 * 100 = " + (4.015 * 100));
            System.out.println("123.3 / 100 = " + (123.3 / 100));//.3
            System.out.println(12.3/100);
        }
    }

    0.05 + 0.01 = 0.060000000000000005
    1.0 - 0.42 = 0.5800000000000001
    4.015 * 100 = 401.49999999999994
    123.3 / 100 = 1.2329999999999999
    0.12300000000000001

    为什么会出现这样的误差?

    其根本原因是计算机所使用二进制01代码无法准确表示某些带小数位的十进制数据。

    那我们应该如何解决这样的误差问题呢?

    于是,引入math.BigDecimal;

    package com.大数计算;
    
    import java.math.BigDecimal;
    //浮点数是不精确的
    public class TestBigDecimal
    {
        public static void main(String[] args) 
        {
            BigDecimal f1 = new BigDecimal("0.05");
            BigDecimal f2 = BigDecimal.valueOf(0.01);
            BigDecimal f3 = new BigDecimal(0.05);
            System.out.println("下面使用String作为BigDecimal构造器参数的计算结果:");
            System.out.println("0.05 + 0.01 = " + f1.add(f2));
            System.out.println("0.05 - 0.01 = " + f1.subtract(f2));
            System.out.println("0.05 * 0.01 = " + f1.multiply(f2));
            System.out.println("0.05 / 0.01 = " + f1.divide(f2));
            
            System.out.println("下面使用double作为BigDecimal构造器参数的计算结果:");
            System.out.println("0.05 + 0.01 = " + f3.add(f2));
            System.out.println("0.05 - 0.01 = " + f3.subtract(f2));
            System.out.println("0.05 * 0.01 = " + f3.multiply(f2));
            System.out.println("0.05 / 0.01 = " + f3.divide(f2));
        }
    }

    下面使用String作为BigDecimal构造器参数的计算结果:
    0.05 + 0.01 = 0.06
    0.05 - 0.01 = 0.04
    0.05 * 0.01 = 0.0005
    0.05 / 0.01 = 5
    下面使用double作为BigDecimal构造器参数的计算结果:
    0.05 + 0.01 = 0.06000000000000000277555756156289135105907917022705078125
    0.05 - 0.01 = 0.04000000000000000277555756156289135105907917022705078125
    0.05 * 0.01 = 0.0005000000000000000277555756156289135105907917022705078125
    0.05 / 0.01 = 5.000000000000000277555756156289135105907917022705078125

    将double类型转换成String类型,在进行运算就可以保证数值的精确,切记不要使用double类型去初始化BigDecimal对象,会产生误差。

  • 相关阅读:
    惊讶
    BLOG休假
    因考试得福
    Shape of My HeartSting !
    一个月的第一天了
    BLOG开张喽~~~
    该走了
    脏话
    EditText的属性
    游戏引擎
  • 原文地址:https://www.cnblogs.com/xiaofengzai/p/11544442.html
Copyright © 2011-2022 走看看