Java float能准确地表示一个分数吗?

Java float能准确地表示一个分数吗?,java,Java,如果我做了类似的事情 final float third = 1f / 3f; System.out.println((third + third + third) == 1.0f); 我明白了。这是否意味着浮点数可以精确代表1/3?很久以前,当我说“不”时,我输了一次赌注!不仅如此,另一方还声称,没有理由认为float不能表示分数。 下面是一个示例程序,它稍微探讨了这个问题: package test; public class FloatTest { public static void

如果我做了类似的事情

final float third = 1f / 3f;
System.out.println((third + third + third) == 1.0f);

我明白了。这是否意味着浮点数可以精确代表1/3?

很久以前,当我说“不”时,我输了一次赌注!不仅如此,另一方还声称,没有理由认为float不能表示分数。 下面是一个示例程序,它稍微探讨了这个问题:

package test;
public class FloatTest
{
public static void main(String[] args)
{
    new FloatTest().run();
}
public void run()
{
    final float third = 1f / 3f;
    final float small = third * .01f;
    final float realSmall = third * .0001f;

    System.out.println("third: " + third);
    System.out.println("small: " + small);
    System.out.println("real small: " + realSmall);
    System.out.println("Three thirds: " + (third + third + third));
    System.out.println("Three small: " + (small + small + small));
    System.out.println("Three real small: " + (realSmall + realSmall + realSmall));
}
}
输出是

third: 0.33333334
small: 0.0033333334
real small: 3.3333334E-5
Three thirds: 1.0
Three small: 0.01
Three real small: 1.00000005E-4
奇怪的结果与以下几件事有关。首先,float不能精确地表示1/3,任何超过1/3的数字都可以用有限的小数位数写出。在过去,由于四舍五入,结果将是.9999999。 在现代,IEEE 754规定了浮点数应该如何表示和算术处理。特别是,在进行算术运算时,会保留额外的三位并执行舍入。这就是为什么前两个结果完全正确,我输了。然而,这些额外的位不能保证准确性,第三个结果显示。 下面是对浮点的一个很好的描述,在舍入sic的底部部分描述了额外的位:
底线是,如果您想要精确的分数值并控制舍入,请使用BigDecimal。

浮点不能精确地表示每个分数,因为浮点数据类型是单精度32位IEEE 754浮点,并且受限制。因此,任何需要大于32位精度的东西都不能用浮点表示。还有double,它是一个双精度64位IEEE 754浮点数。最后,Java具有任意精度的BigDecimal类。然而,它也不能完美地代表每一个分数,考虑一下;如果您尝试计算,BigDecimal将引发异常

浮点数不能精确表示1/3。如果A/B是简化表示法中的分数,且A和B都是整数,A>0,B>1,则浮点数只能表示B为2到2126的幂,且A小于224的分数;或者在其他情况下,B是2的幂,大于2126,但我不会详细讨论它们是什么。我没有考虑B=1;需要额外的工作来描述什么是整数float能够表示的,这与这里无关

因此,float不能表示1/3。如果计算f=1/3,则浮点表示的分数为11184811/33554432 33554432=225。如果将f+f相加,结果是一个表示分数11184811/16777216 16777216=224的浮点。如果将其添加到f中,则得到的精确分数将是33554433/33554432。但是这个分数不能用浮点表示,因为它违反了第一段33554433>224中的规则。因此,结果必须四舍五入到可以用浮点表示的值,并且该值将为1

试试这个:

final float third = 1f / 3f;
System.out.println(((double)third + (double)third + (double)third) == 1.0);

如果第三个数字正好代表1/3,那么将其转换为双精度数字也正好代表1/3,因为双精度数字比浮点数字更精确,对吗?但这是错误的。事实上,如果您显示,左侧的double是1.0000000298023224。也就是说,33554433/33554432。

浮点数可以精确表示1/2,如0.5f。某些分数可以精确表示。我认为它可能仅限于除数为二的幂的分数,但IEEE float有一种让你惊讶的方式,所以可能还有其他的。你应该读。@HotLicks不,没有其他的。如果A/B是简化形式的分数,那么如果B是2到2^126的幂,如果A/B小于2^24,则可以精确表示。因此,2^24-1/2^43可以精确表示,但2^24+1/2^43不能。我忽略了整数,即B=1。还有其他分数,其中B是2的幂>=2^127,但它仍然必须是2的幂。为什么这个答案被否决了?听起来不错。当然,BigDecimal不会比float更精确地表示1/3,尽管如果您愿意,可以获得更高的精度。如果你真的想精确地表示有理数,请看。你是说BigDecimal可以完美地表示1/3吗?我不这么认为。另外,你能给我们展示一些代码,演示当你试图计算黄金分割率时BigDecimal抛出异常的情况吗?不,它不能。代码不是我想的那样;但我记得我用的是牛顿的方法。