为什么浮动是双重铸造的不精确,双重浮动不是?

时间:2013-11-16 15:58:56

标签: java casting floating-point double

我知道浮动点变量在投射时会失去精度。但我不明白的是,为什么从较小的原语到较大的原语的演员是不确定的,反之亦然。我会理解,如果它发生在从double到float,但它是另一种方式。为什么会这样?

查看这两项测试的结果:

@Test
public void castTwoPrimitiveDecimalsUnpreciseToPrecise()
{
    float var1 = 6.2f;
    double var2 = var1;

    assertThat(var2, is(6.2d)); //false, because it's 6.199999809265137
}

@Test
public void castTwoPrimitiviesDecimalsPreciseToUnpresice()
{
    double var1 = 7.6d;
    float var2 = (float)var1;

    assertThat(var2, is(7.6f)); //true
}

2 个答案:

答案 0 :(得分:7)

精度问题在于变量的初始化,而不是转换。

在第一种情况下,您从一个只是十进制6.2的浮点近似的数字开始。对double的转换得到一个double,其值与float近似值完全相同。然后将它与更接近的双近似值进行比较,因此它当然不匹配。

在第二种情况下,您从十进制7.6的双重近似开始。然后将其转换为float。这会将双倍数转为浮点数。舍入两次,在原始转换为double和在转换为浮动时,可以想象产生一个不同的答案,从直接将数字转换为浮点数,但通常你会得到浮点近似值。然后你将它与浮动近似值进行比较,所以你得到一个匹配就不足为奇了。

答案 1 :(得分:1)

假设有人用尺子测量钉子,并确定它的直径为3/8“。另一个人用卡尺测量一个孔并发现它是9.5267mm。钉子是否适合孔?

如果将较粗糙的测量值转换为更高精度的形状,则会发现孔看起来比栓钉大0.0017mm(即大约1/15000“)。如果将它们转换为较低精度的形状,人们会发现价值无法区分。

如果测量为3/8“的挂钩实际上精确地为9.525mm [精确度量当量],则这种测量可以转换为更高分辨率的形式而不会造成转换损失。但是,如果它只是意味着“接近3/8”标记的东西比23/64“或25/64”更接近,这样的转换将导致通常预期的近似值被解释为比实际更精确。

有些人会认为钉子和孔被认为是不同尺寸的事实。就个人而言,我认为将它们视为无法区分会更好。根据所述的测量结果,没有特别的理由相信任何确定的钉子比孔大;它可能并不完全相同,但称它们难以区分比准备更大更准确。

就个人而言,我讨厌需要将值写入或转换为[single-precision] float的语言规则,仅仅是为了关闭编译器。如果想要将float设置为最接近4.7的值,则应该能够简单地说:

float f=4.7;

并实现这一效果。看到的人:

static final float coef = 4.7f;
... some time later
float  f = coef;
double d = coef;

无法知道目标是将d设置为double值等于最接近4.7的float值,还是目标是设置{{ 1}}到最接近4.7的d值。遗憾的是,Java没有提供可以声明一个常量的方法,该常量可以在没有显式强制转换的情况下静默分配给double,但在分配给float时将使用该类型中可用的精度。

顺便提一下,如果我的目标是设置一些double变量double等于最接近指定数值v的{​​{1}}的值,而不是值最接近该数值的float,我可能会非常明确地编码:

coef

这将清楚明确地表明程序员期望并且打算为double分配一个先前舍入为v = (double)(float)coef; 精度的值的事实。如果缺少double类型转换,则必须考虑程序员可能期望float成为(double)的可能性(例如,因为编写代码时,它是{{1}但是从那时起它被改为v)。如果没有float类型转换,则必须考虑程序员期望float成为double的可能性(例如因为它已经存在,但有人将其更改为浮点数以允许它将被分配给(float)类型的变量,而无需编译器发出嘎嘎声。)