拓宽数值类型会造成精度丢失吗?

本文探讨了Java中数值拓宽类型转化时的数据精度丢失现象,通过实例代码展示了int值自动提升为float时如何造成精度丢失,并解释了背后的原理。

    Java语言的8种基本数据类型中7种都可以看作是数值类型,我们知道对于数值类型的转换有一个规律:从窄范围转化成宽范围能够自动类型转换,反之则必须强制转换。请看下图:

byte-->short-->int-->long-->float-->double

char-->int

我们把顺箭头方向的转化叫做拓宽类型,逆箭头方向的转化叫做窄化类型。一般我们认为因为顺箭头方向的转化不会有数据和精度的丢失,所以Java语言允许自动转化,而逆箭头方向的转化可能会造成数据和精度的丢失,所以Java语言要求程序员在程序中明确这种转化,也就是强制转换。那么拓宽类型就一定不会造成数据和精度丢失吗?请看下面代码:

int i=2000000000;

int num=0;

for(float f=i;f<i+50;f++){

    num++;

}

System.out.println(num);

请考虑以上代码输出多少?

如果你回答50 ,那么请运行一下,结果会让你大吃一惊!没错,输出结果是0,难道这个循环根本就没有执行?确实如此,如果你还不死心,我带你看一个更诧异的现象,运行以下代码,看输出什么?

int i=2000000000;

float f1=i;

float f2=i+50;

System.out.println(f1==f2);

    结果竟然是true;难道f1f2是相等的吗?是的,这也就能解释为什么上一段代码输出的结果是0,而不是50了。那为什么会这样呢?关键原因在于你将int值自动提升为float时发生了数据精度的丢失i的初始值是2000000000,这个值非常接近Integer.MAX_VALUE,因此需要用31位来精确表示,而float只能提供24位数据的精度(另外8位是存储位权,见IEEE745浮点数存储规则)。所以在这种自动转化的过程中,系统会将31位数据的前24位保留下来,而舍弃掉最右边的7位,所以不管是2000000000还是2000000050,舍弃掉最右边7位后得到的值是一样的。这就是为什么f1==f2的原因了。

    类似的这种数值拓宽类型的过程中会造成精度丢失的还有两种情况,那就是long转化成floatlong转化成double,所以在使用的时候一定要小心。 

### 类型拓宽的定义 类型拓宽(Type Widening)是指在 TypeScript 中,当声明一个变量并赋值时,TypeScript 编译器会根据赋值的值推断出一个比字面量类型更宽泛的类型。这是为了提供一定的灵活性,允许变量在后续使用中可以被赋予该宽泛类型内的其他值。 例如: ```typescript let num = 1; // 这里 num 的类型拓宽为 number,而不是字面量类型 1 ``` 在这个例子中,虽然初始值是 `1`,但 TypeScript 推断 `num` 的类型为 `number`,意味着 `num` 后续可以被赋予任何 `number` 类型的值,如 `2`、`3.14` 等。 再如对象和数组: ```typescript let obj = { name: 'John' }; // obj 的类型拓宽为 { name: string } let arr = ['apple']; // arr 的类型拓宽为 string[] ``` ### 避免不必要类型拓宽的方法 #### 使用 `const` 声明 使用 `const` 声明常量时,TypeScript 会尽可能地推断出最精确的字面量类型。 ```typescript const num = 1; // num 的类型为字面量类型 1 const obj = { name: 'John' } as const; // obj 的类型为 { readonly name: 'John' } const arr = ['apple'] as const; // arr 的类型为 readonly ['apple'] ``` #### 使用 `as const` 断言 `as const` 断言可以将值视为不可变的常量,让 TypeScript 对值进行更精确的类型推断,将值的类型固定为字面量类型,并且将数组转换为只读元组,对象属性转换为只读属性。 ```typescript let person = { name: 'Alice', age: 25 } as const; // person 的类型为 { readonly name: 'Alice'; readonly age: 25 } let fruits = ['banana', 'cherry'] as const; // fruits 的类型为 readonly ['banana', 'cherry'] ``` #### 明确指定类型 在声明变量时,明确指定变量的类型,避免编译器进行类型拓宽。 ```typescript let specificNum: 1 = 1; // specificNum 的类型为字面量类型 1 let specificObj: { name: 'John' } = { name: 'John' }; // specificObj 的类型为 { name: 'John' } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值