精度损失 - int - >漂浮或加倍

时间:2010-05-06 12:35:00

标签: java floating-point double precision

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

  

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

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

现在我有点不清楚我是否在这里击中了正确的区域。我非常肯定它会失去精确度,但我无法完全理解为什么。我可以帮忙吗?

9 个答案:

答案 0 :(得分:49)

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

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

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

另请参阅维基百科上的“Floating Point

答案 1 :(得分:23)

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

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

类似的推理可以与longdouble一起使用。

答案 2 :(得分:18)

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

JLS 5.1.2 Widening primitive conversion

  

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

     
      
  • intlongfloatdouble
  •   
  • (其余省略)
  •   
     

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

     

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

     

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

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

打印:

-46
     

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

答案 3 :(得分:13)

不,floatdouble也是固定长度的 - 他们只是以不同的方式使用它们的位。详细了解它们在the Floating-Poing Guide中的运作方式。

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

答案 4 :(得分:4)

可能是我见过的最明确的解释: 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,所以高于这个值你很快会有多个整数值映射到每个可用的浮点数,因此精度会降低。

答案 5 :(得分:4)

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

  

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

不正确。
示例:这是一个大于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位,由下式表示:
符号*尾数* 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)不正确

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

对于这些示例,我使用的是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将失去精度。

答案 8 :(得分:-1)

您可以将double分配为int而不丢失精度。