Java:将浮点数转换为双保留小数点精度

问题描述 投票:0回答:3

我有一个基于

float
的存储decimal的自然数。
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;

更新一:

我很清楚 0.1f != 0.1d。这个问题不是关于精确的小数计算。可悲的是,这个问题被否决了。我会尝试再次解释......

假设我使用的 API 返回

float
十进制 MSFT 股票价格的数字。信不信由你,这个 API 存在:

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

众所周知,MSFT 股票的价格是一个十进制数,不超过 5 位数,例如31.455、50.12、45.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));
}
java floating-point decimal precision floating-point-conversion
3个回答
3
投票

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

当您将

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

时没有打印这些数字

忽略这些数字并根据需要使用

double
进行计算。问题不在转换中,而是在打印功能中。


1
投票

据我了解,您知道

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

要得到两位小数,你可以乘以100,

rint
/
Math.round
结果,再乘以
0.01
得到你想要的接近的
double
。 (为了得到最接近的,除以
100.0
。)但我怀疑你已经知道这一点并且正在寻找更快的东西。试试
((9007199254740992 + 100.0 * x) - 9007199254740992) * 0.01
,不要弄乱括号。也许
strictfp
黑客攻击的好措施。

您说了五个重要数字,显然您的问题不仅限于 MSFT 的股价。直到

double
s 不能准确地表示 10 的幂,这还算不错。 (也许这也适用于超出该阈值。)
float
的指数域将所需的 10 次方缩小为 2,并且有 256 种可能性。 (次正规情况除外。)获得正确的 10 次方只需要一个条件,并且舍入技巧非常简单。

所有这些都会变得一团糟,我建议你坚持使用

toString
方法来处理所有奇怪的情况。


1
投票

如果您的目标是拥有一个

double
其规范表示将与
float
的规范表示相匹配将浮点数转换为字符串并将结果转换回双精度可能是实现该结果的最准确方法,至少什么时候可能(我不确定 Java 的双字符串逻辑是否会保证不会有一对连续的
double
值,它们将自己报告为恰好高于和低于具有五个重要数字的数字数字)。

如果您的目标是四舍五入到五位有效数字,而已知该值在

float
形式中四舍五入到五位有效数字,我建议最简单的方法可能是简单地四舍五入到五位有效数字。如果你的数字大小大致在 1E+/-12 范围内,首先找到比你的数字小的 10 的最小次方,乘以 100,000,再乘以你的数字,四舍五入到最接近的单位,然后除以十的幂。因为除法通常比乘法慢得多,所以如果性能很重要,您可以保留一个包含 10 的幂及其倒数的表。为避免出现舍入错误的可能性,您的表应存储最接近其倒数的 2 的幂
double
的每个幂,然后存储最接近第一个
double
与实际值之间差值的
double
互惠的。因此,100 的倒数将存储为 0.0078125 + 0.0021875;值 n/100 将计算为
n*0.0078125 + n*0.0021875
。第一项永远不会有任何舍入误差(乘以 2 的幂),而第二个值的精度将超过最终结果所需的精度,因此最终结果应该准确舍入。

© www.soinside.com 2019 - 2024. All rights reserved.