JDK源码解析(5)——Short、Long、Double、Float

本文深入解析了JDK中Short、Long、Double和Float四个数值类型的源码,包括它们的方法如decode、reverseBytes、bitCount、compareTo等,并探讨了浮点数的精度丢失问题和Infinity、NaN的表示。文章强调了valueOf方法的缓存机制以及在处理精度问题时使用BigDecimal的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本系列是对JDK源码中8种数据类型的简单了解。

JDK源码解析(1)——Integer

JDK源码解析(2)——Boolean

JDK源码解析(3)——String

JDK源码解析(4)——Byte、Character

JDK源码解析(5)——Short、Long、Double、Float

一、Short

1、shortcache的初始值在-128 —— 127,不同于Integer,shortcache不可调整大小。

decode

//decode方法将字符串解码为Short,
//此方法接受十进制字符串,十六进制字符串(用0x\0X#标识),八进制字符串(用0标识),
//但是解码后的值必须在[-32768,32767]这一区间内,否则抛出NumberFormatException异常。
public static Short decode(String nm) throws NumberFormatException {
	int i = Integer.decode(nm);
	if (i < MIN_VALUE || i > MAX_VALUE)
		throw new NumberFormatException(
				"Value " + i + " out of range from input " + nm);
	return valueOf((short)i);
}

reverseBytes

//将short二进制的高八位和低八位交换。
public static short reverseBytes(short i) {
    return (short) (((i & 0xFF00) >> 8) | (i << 8));
}

toUnsignedInt

//     将short类型的数转换为无符号的int类型。
public static int toUnsignedInt(short x) {
    return ((int) x) & 0xffff;
}

toUnsignedLong

//将short类型的数转换为无符号的long类型。
public static long toUnsignedLong(short x) {
    return ((long) x) & 0xffffL;
}

 

二、Long

类的定义

public final class Long extends Number implements Comparable<Long> 

valueOf

//推荐使用 valueOf 方法,少使用 parseLong 方法
//valueOf 方法会从缓存中去拿值,如果命中缓存,会减少资源的开销,parseLong 方法就没有这机制。
 public static Long valueOf(long l) {
        final int offset = 128;
     	// 先在缓冲池中取
        if (l >= -128 && l <= 127) {
            // 这里+offest是要计算出索引,因为cache[0]=-127
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
}

bitCount

//返回二进制中1的个数。
public static int bitCount(long i) {
	i = i - ((i >>> 1) & 0x5555555555555555L);
	i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
	i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
	i = i + (i >>> 8);
	i = i + (i >>> 16);
	i = i + (i >>> 32);
	return (int)i & 0x7f;
}

三、Double

类的定义

public final class Double extends Number implements Comparable<Double> 

max

//a与b都为0.0时,a为-0则返回b
    public static double max(double a, double b) {
        if (a != a)
            return a;   // a is NaN
        if ((a == 0.0d) &&
            (b == 0.0d) &&
            (Double.doubleToRawLongBits(a) == negativeZeroDoubleBits)) {
            // Raw conversion ok since NaN can't map to -0.0.
            return b;
        }
        return (a >= b) ? a : b;
    }

min

//a与b都为0.0时,b为-0则返回b
    public static double min(double a, double b) {
        if (a != a)
            return a;   // a is NaN
        if ((a == 0.0d) &&
            (b == 0.0d) &&
            (Double.doubleToRawLongBits(b) == negativeZeroDoubleBits)) {
            // Raw conversion ok since NaN can't map to -0.0.
            return b;
        }
        return (a <= b) ? a : b;
    }

double的精度丢失问题

1、精度丢失

当我们在计算 0.1+0.2 时,结果不是 0.3,而是:0.30000000000000004

2、分析

由于我们输入的十进制的 double 类型的数据在进行计算的时候,计算机会先将其转换为二进制数据,然后再进行相关的运算。然而在十进制转二进制的过程中,有些十进制数是无法使用一个有限的二进制数来表达的,换言之就是转换的时候出现了精度的丢失问题

3、解决

将 double 类型的数据转换成 BigDecimal 来进行运算,最后再转换成 double 类型的数据。

double d1 = 0.1, d2 = 0.2;
System.out.println(d1 + d2);
System.out.println(new BigDecimal(d1).add(new BigDecimal(d2)).doubleValue());
System.out.println(BigDecimal.valueOf(d1).add(BigDecimal.valueOf(d2)).doubleValue());
System.out.println(new BigDecimal(Double.toString(d1)).add(new BigDecimal(Double.toString(d2))).doubleValue());
//print
0.30000000000000004
0.30000000000000004
0.3
0.3

为什么会出现用BigDecimal计算出 0.30000000000000004 ,这个值呢。我们看下面的源码:

// 方式一    
public BigDecimal(double val) {
    this(val,MathContext.UNLIMITED);
}

// 方式二
public BigDecimal(String val) {
    this(val.toCharArray(), 0, val.length());
}

// 方式三(其实底层就是方式二)
public static BigDecimal valueOf(double val) {
    return new BigDecimal(Double.toString(val));
}

我们可以看到,如果一的构造方式去实现,将会用double类型来计算,如果我们用方式二去构造或者使用方法三valueOf(本质上是方法二),计算就不会产生精度丢失问题。

四、Float

类的定义

public final class Float extends Number implements Comparable<Float>

构造方法

public Float(float value) {
	this.value = value;
}
public Float(double value) {
	this.value = (float)value;
}
public Float(String s) throws NumberFormatException {
	value = parseFloat(s);
}
public static float parseFloat(String s) throws NumberFormatException {
	return FloatingDecimal.parseFloat(s);
}

valueOf

//Float调用valueOf都是new实例。
public static Float valueOf(String s) throws NumberFormatException {
	return new Float(parseFloat(s));
}
public static Float valueOf(float f) {
	return new Float(f);
}

compareTo

//f1小于f2则返回-1,反之则返回1。
//无法通过上述直接比较时则使用floatToIntBits方法分别将f1和f2转成IEEE 754标准对应的整型数,然后再比较。相等则返回0,否则返回-1或1。注意:正0比负0大;NaN比任何非NaN的数都大。
public int compareTo(Float anotherFloat) {
	return Float.compare(value, anotherFloat.value);
}
public static int compare(float f1, float f2) {
	if (f1 < f2)
		return -1;           // Neither val is NaN, thisVal is smaller
	if (f1 > f2)
		return 1;            // Neither val is NaN, thisVal is larger

	// 不能使用floatToRawIntBits方法,因为可能为NaN.
	int thisBits    = Float.floatToIntBits(f1);
	int anotherBits = Float.floatToIntBits(f2);

	return (thisBits == anotherBits ?  0 : // Values are equal
			(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
			 1));                          // (0.0, -0.0) or (NaN, !NaN)
}

五、Infinity和NaN

Infinity

infinity表示无穷,如果我们用浮点数除以‘0’,一般用infinity表示正无穷,-infinity 表示负无穷

Float d1 = 1.00f, d2 = 0.00f;
System.out.println(d1/d2);
//print
Infinity

Float d1 = -1.00f, d2 = 0.00f;
System.out.println(d1/d2);
//print
-Infinity

NaN

        NaN是“IEEE 754”协议中规定了几种特殊值,
        1. 第一种 指数是0并且尾数的小数部分是0,这个数±0(和符号位相关)
        2. 第二种 指数2e-1并且尾数的小数部分是0,这个数是±∞(同样和符号位相关)
        3. 第三种 指数2e-1并且尾数的小数部分非0,这个表示为不是一个数(NaN)

Float d1 = 0.00f, d2 = 0.00f;
System.out.println(d1/d2);
//print
NaN

/**
NaN的产生情形:
1. 至少有一个参数是NaN的运算
2. 不定式
    2.1 下列除法运算:0/0、∞/∞、∞/−∞、−∞/∞、−∞/−∞
    2.2 下列乘法运算:0×∞、0×−∞
    2.3 下列加法运算:∞ + (−∞)、(−∞) + ∞
    2.4 下列减法运算:∞ - ∞、(−∞) - (−∞)
3. 产生复数结果的实数运算。例如:
    3.1 对负数进行开偶次方的运算
    3.2 对负数进行对数运算
    3.3 对正弦或余弦到达域以外的数进行反正弦或反余弦运算
**/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值