Java:将float转换为double,保留小数点精度

时间:2014-06-15 19:38:46

标签: java floating-point decimal floating-point-precision floating-point-conversion

我有一个基于float十进制存储的性质数字。 float的精度可以满足我的需求。现在我想要使用double对这些数字执行一些更精确的计算。

一个例子:

float f = 0.1f;
double d = f; //d = 0.10000000149011612d
// but I want some code that will convert 0.1f to 0.1d;

更新1:

我非常清楚0.1f!= 0.1d。这个问题不是关于精确的十进制计算。可悲的是,这个问题被低估了。我会再试一次解释......

我们说我使用API​​返回float个数字,用于小数MSFT股票价格。不管你信不信,这个API存在:

interface Stock {
    float[] getDayPrices();
    int[] getDayVolumesInHundreds();
}

众所周知,MSFT份额的价格是十进制数字,不超过5位,例如31.455,50.12,125.888。显然,API不能与BigDecimal一起使用,因为只是为了传递价格,这将是一个很大的开销。

我们还要说我想以double精度计算这些价格的加权平均值:

float[] prices = msft.getDayPrices();
int[] volumes = msft.getDayVolumesInHundreds();
double priceVolumeSum = 0.0;
long volumeSum = 0;
for (int i = 0; i < prices.length; i++) {
    double doublePrice = decimalFloatToDouble(prices[i]);
    priceVolumeSum += doublePrice * volumes[i];
    volumeSum += volumes[i];
}
System.out.println(priceVolumeSum / volumeSum);

我需要decimalFloatToDouble

的高性能实现

现在我使用以下代码,但我需要一些更聪明的东西:

double decimalFloatToDouble(float f) {
    return Double.parseDouble(Float.toString(f));
}

3 个答案:

答案 0 :(得分:2)

编辑:这个答案对应于最初措辞的问题。

当您将0.1f转换为double时,您将获得相同的数字,即单精度中有理1/10(不能以任何精度以二进制表示)的不精确表示。唯一改变的是打印功能的行为。您看到的数字0.10000000149011612已经位于float变量f中。它们根本没有打印,因为打印float时不会打印这些数字。

忽略这些数字,并根据需要使用double进行计算。问题不在转换中,而是在打印功能中。

答案 1 :(得分:1)

据我了解,你知道floatfloat - 一个整数千分之一的ulp内,你知道你在没有两个整数的范围内百分之一映射到相同的float。所以信息根本没有消失;你只需要弄清楚你有哪个整数。

要获得两位小数,您可以乘以100,rint / Math.round结果,然后乘以0.01得到一个近似double。通缉。 (为了得到最接近的,除以100.0。)但我怀疑你已经知道这一点,并且正在寻找更快的东西。试试((9007199254740992 + 100.0 * x) - 9007199254740992) * 0.01 ,不要弄乱括号。也许strictfp可以解决好的问题。

您说了五个重要数字,显然您的问题不仅限于MSFT股价。直到double s不能完全代表10的幂,这不是太糟糕。 (也许这也超出了这个阈值。)float的指数字段将所需的十次幂缩小为两个,并且有256种可能性。 (除了在低于正常的情况下。)获得10的正确权力只需要一个条件,并且舍入技巧很简单。

所有这一切都将变得一团糟,我建议你坚持使用toString方法处理所有奇怪的案例。

答案 2 :(得分:1)

如果你的目标是拥有一个double,其标准表示将匹配float的规范表示将float转换为字符串并将结果转换回double可能是最准确的方式实现这一结果,至少在可能的情况下(我不确定Java的双线串逻辑是否能保证不会成为一对连续{ {1}}值将自己报告为刚好在上面,并且恰好低于一个有五位有效数字的数字。)

如果您的目标是舍入到五个有效数字,一个已知的值已经以double形式四舍五入到五个有效数字,我建议最简单的方法可能是简单地舍入到五个有效数字数据。如果您的数字幅度大致在1E +/- 12范围内,那么首先找到小于您的数字的10的最小幂,将其乘以100,000,乘以您的数字,舍入到最接近的单位,并且除以十的力量。因为除法通常比乘法慢得多,如果性能至关重要,你可以保留一个权力为10的表和它们的倒数。为了避免舍入错误的可能性,您的表应该为每个幂的最接近幂float存储到它的倒数,然后最接近double到第一个double之间的差值。 1}}和实际的倒数。因此,100的倒数将被存储为0.0078125 + 0.0021875;值n / 100将计算为double。第一项永远不会有任何舍入误差(乘以2的幂),第二项的精度将超过最终结果所需的精度,因此最终结果应该精确舍入。