精度损失 – int – > float或double

我有一个我正在修改的考试题目,问题是4分。

“在java中,我们可以将int赋给double或float”。 这会丢失信息吗?为什么?

我已经说过,因为int通常具有固定的长度或大小 – 存储数据的精度是有限的,其中以浮点存储信息可能是无限的,基本上我们丢失信息因为这个

现在我有点粗略地说我是否在这里击中了正确的区域。 我确信它会失去精确度,但我无法完全理解为什么。 我能得到一些帮助吗?

在Java中,Integer使用32位来表示其值。

在Java中,FLOAT使用23位尾数,因此大于2 ^ 23的整数将截断其最低有效位。 例如,33554435(或0x200003)将被截断为大约33554432 +/- 4

在Java中,DOUBLE使用52位尾数,因此能够表示32位整数而不会丢失数据。

另请参阅维基百科上的“ 浮点 ”

没有必要知道浮点数的内部布局。 您所需要的只是鸽子原理以及intfloat大小相同的知识。

  • int是32位类型,每个位模式表示一个不同的整数,因此有2 ^ 32个int值。
  • float是32位类型,因此它最多有2 ^ 32个不同的值。
  • 一些float表示非整数,因此表示整数的float少于 2 ^ 32。
  • 因此,不同的int值将转换为相同的float (=精度损失)。

类似的推理可以用于longdouble

以下是JLS对此事的评论(在非技术性讨论中)。

JLS 5.1.2拓宽原始转换

以下19种关于基本类型的特定转换称为扩展基元转换:

  • intlongfloatdouble
  • (其余省略)

intlong值转换为float ,或将long值转换为double ,可能会导致精度损失 – 也就是说,结果可能会丢失该值的一些最低有效位。 在这种情况下,使用IEEE 754舍入到最接近模式,得到的浮点值将是整数值的正确舍入版本。

尽管可能发生精度损失,但原始类型之间的扩展转换不会导致运行时exception。

这是一个失去精度的扩展转换的示例:

 class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } } 

打印:

 -46 

因此表明在从类型int转换为float类型的过程中信息丢失,因为float类型的值不精确到九位有效数字。

不, floatdouble也是固定长度的 – 它们只是使用不同的位。 阅读有关它们在Floating-Poing指南中的确切工作原理的更多信息。

基本上,在将double int double时不会失去精度,因为double有52位精度,足以容纳所有int值。 但是float只有23位的精度,因此它不能精确地表示大于约2 ^ 23的所有int值。

可能是我见过的最明确的解释: http : //www.ibm.com/developerworks/java/library/j-math2/index.html ULP或精度最低的单位定义了任何两个浮点值之间的可用精度。 随着这些值的增加,可用精度降低。 例如:介于1.0和2.0之间,有8,388,609个浮点数,1,000,000和1,000,001之间有17个。在10,000,000个ULP是1.0,所以高于这个值你很快会有多个整数值映射到每个可用的浮点数,因此精度会降低。

你的直觉是正确的,你可以在将int转换为float时松开精度。 然而,它并不像大多数其他答案中那样简单。

在Java中,FLOAT使用23位尾数,因此大于2 ^ 23的整数将截断其最低有效位。 (来自本页的post)

不对。
示例:这是一个大于2 ^ 23的整数,它转换为一个没有丢失的浮点数:

 int i = 33_554_430 * 64; // is greater than 2^23 (and also greater than 2^24); i = 2_147_483_520 float f = i; System.out.println("result: " + (i - (int) f)); // Prints: result: 0 System.out.println("with i:" + i + ", f:" + f);//Prints: with i:2_147_483_520, f:2.14748352E9 

因此,大于2 ^ 23的整数将截断其最低有效位并不是真的。

我找到的最佳解释是:
Java中的浮点数为32位,表示为:
sign *尾数* 2 ^指数
标志*(0至33_554_431)* 2 ^( – 125至+127)
资料来源: http : //www.ibm.com/developerworks/java/library/j-math2/index.html

为什么这是一个问题?
它留下的印象是, 只要查看 int的大小,就可以确定是否存在从int到float的精度损失。
我特别看到了Java考试问题,其中一个被问到一个大的int是否会转换为一个没有丢失的浮点数。

此外,有时人们倾向于认为从int到float会有精度损失:
当int大于:1_234_567_890 不为真时 (参见上面的反例)
当int大于:2指数23(等于:8_388_608) 不为真
当int大于:2指数24(等于:16_777_216)时不为真

结论
从足够大的整数到浮点数的转换可能会失去精度。
仅仅通过查看 int的大小来确定是否会有损失(即不试图深入到实际的浮点表示中)是不可能的。

将int赋值给double或float可能会失去精度有两个原因:

  • 有些数字不能表示为double / float,因此它们最终会近似
  • 大整数可能在租约有效数字中包含太多精度

对于这些示例,我使用的是Java。

使用这样的函数来检查从int转换为float时的精度损失

 static boolean checkPrecisionLossToFloat(int val) { if(val < 0) { val = -val; } // 8 is the bit-width of the exponent for single-precision return Integer.numberOfLeadingZeros(val) + Integer.numberOfTrailingZeros(val) < 8; } 

使用这样的函数来检查从长到两次铸造时的精度损失

 static boolean checkPrecisionLossToDouble(long val) { if(val < 0) { val = -val; } // 11 is the bit-width for the exponent in double-precision return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 11; } 

使用这样的函数来检查从long转换为float时的精度损失

 static boolean checkPrecisionLossToFloat(long val) { if(val < 0) { val = -val; } // 8 + 32 return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 40; } 

对于这些函数中的每一个,返回true意味着将该整数值强制转换为浮点值将导致精度损失。

如果积分值超过24个有效位,则转换为float将失去精度。

如果积分值超过53个有效位,则转换为double将失去精度。