繁体   English   中英

Java中的数学 - 不同对象的不同结果

[英]Math in Java - different results with different objects

我正在为我正在进行的应用程序计算得到一些奇怪的结果,我认为这里的某个人可能能够帮助弄清楚发生了什么。

此特定计算的要求表明计算应如下所示:

A和B是已知的

A * B = C

对于这个特定的计算

A = 0.0410
B = 123456789010

以下是我看到的结果:

计算器:

0.0410 * 123456789010 = 5061728349.41

Java的:

B是双:

0.0410f * 123456789010d = 5.061728489223363E9 = 5061728489.223363

B很长:

0.0410f * 123456789010l = 5.0617288E9 

对于我来说,精度的损失并不重要(我只需要9位数的精度),而不是10s和1s点的差异。 为什么使用double进行计算会给出“错误”结果?

顺便说一句,我尝试使用BigDecimal进行计算,并得到与使用double时相同的结果。

发生的各种类型转换由JLS#5.6.2指定。 在你的情况下(摘录):

  • 如果任一操作数的类型为double,则另一个操作数转换为double。
  • 否则,如果任一操作数的类型为float,则另一个操作数转换为float。

0.0410f * 123456789010d = 506172848.9223363 ,首先将0.0410f转换为不必等于0.0410d 实际上你可以尝试一下,看看那不是:

    double d1 = 0.041d;
    double d2 = 0.041f;
    System.out.println(new BigDecimal(d1));
    System.out.println(new BigDecimal(d2));

输出:

0.041000000000000001720845688168992637656629085540771484375
0.041000001132488250732421875

在下一个示例中:

0.0410f * 123456789010L = 506172832

long被转换为float,您可以使用此示例进行验证:

    float f1 = 0.0410f;
    float f2 = 123456789010L;
    System.out.println(new BigDecimal(f1)); // 0.041000001132488250732421875
    System.out.println(new BigDecimal(f2)); // 123456790528
    System.out.println(new BigDecimal(0.0410f * 123456789010L)); // 5061728768
    System.out.println(new BigDecimal(f1 * f2)); // 5061728768

至于浮动/双重操作的精度,请检查这个问题

最后,如果你使用BigDecimal,你会得到正确的答案:

    BigDecimal a = new BigDecimal("0.041");
    BigDecimal b = new BigDecimal("123456789010");
    System.out.println(a.multiply(b)); // outputs 5061728349.410

TLDR答案:浮动不再能够准确地代表“正确”的答案。 请改用双。 此外,乘法也将在没有明确演员的情况下完成。

答案我使用http://www.ideone.com

  A      B     C      
float  long  float  5061728768.000000
double long  double 5061728489.223363

问题是浮点数的精度远小于一倍,所以当乘以一个大数(例如你的10 ^ 10值)时,你会在乘法中失去这个精度。 如果我们为乘法显式地将A转换为double:

double C = ((double)A)*B; //=5061728489.223363

然后我们回到额外的精度。 如果我们将双回答转回浮点数:

float C = (float)((double)((double)A)*B); //=5061728256.000000 

你看到答案又不同了。 使用乘法的结果类型,因此在这个实例中为double ,但是回退到float会降低精度。 如果没有明确的大小写( double C=A*B ),则使用float类型。 the multiplication. 对于两个强制转换,乘法都是双精度,乘法精度会丢失。

第一个计算是使用double(64位),第二个浮点数(32位)。 你所看到的是“四舍五入的错误”。

在这两种情况下,它都是浮点计算,但在第二种情况下,不涉及“双”参数,因此它只使用32位算术。

引用Java语言规范

如果二元运算符的至少一个操作数是浮点类型,则操作是浮点运算,即使另一个是整数。

如果数值运算符的至少一个操作数是double类型,则使用64位浮点运算执行运算,并且数值运算符的结果是double类型的值。 如果另一个操作数不是double,则首先将其扩展(第5.1.5节)以通过数字提升(第5.6节)键入double。

否则,使用32位浮点运算执行运算,并且数值运算符的结果是float类型的值。 (如果另一个操作数不是浮点数,则首先将其扩展为通过数字提升键入float。)

您的问题的答案可能在Java语言规范的浮点操作部分这篇较旧的帖子中 由于隐含的转换正在发生,您可能会遇到舍入错误。

适用于您的情况的报价是

第三次操作:

如果二元运算符的至少一个操作数是浮点类型,则操作是浮点运算,即使另一个是整数。

第二次操作:

如果数值运算符的至少一个操作数是double类型,则使用64位浮点运算执行运算,并且数值运算符的结果是double类型的值。 如果另一个操作数不是double,则首先将其扩展(第5.1.5节)以通过数字提升(第5.6节)键入double。

第一次操作

否则,使用32位浮点运算执行运算,并且数值运算符的结果是float类型的值。 (如果另一个操作数不是浮点数,则首先将其扩展为通过数字提升键入float。)

因此,您不应该担心,但如果需要,可以决定您想要的精度是多少并使用适当的铸件。

32位IEEE浮点数具有7位精度; 64位允许16.这就是你得到的。 如果这些都不够,则必须使用BigDecimal。

在实现IEE标准的每种语言中都是如此,而不仅仅是Java。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM