将浮点数转换为两倍而不损失精度


97

我有一个原始浮点数,需要作为原始双数。简单地将浮法转换为两倍会给我带来额外的精度。例如:

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

但是,如果我不是强制类型转换,而是将浮点数输出为字符串,然后将字符串解析为双精度,那么我得到的是:

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

有没有比String返回更好的方法了?

Answers:


124

这并不是说您实际上获得了更高的精度-而是浮标没有准确地代表您最初瞄准的数字。双精确地表示原始浮子; toString显示的是已经存在的“额外”数据。

例如(假设这些数字不正确,我只是在编造东西),假设您有:

float f = 0.1F;
double d = f;

则值f可能恰好是0.100000234523。d将具有完全相同的值,但是当您将其转换为字符串时,它将“信任”精度更高的精度,因此不会尽早进行四舍五入,并且您会看到已经有“多余的数字”在那里,但是对你隐藏。

当您转换为字符串并返回时,最终会得到一个比原始浮点数更接近字符串值的双精度值-但这仅您真的相信字符串值是您真正想要的值时才有用。

您确定float / double是此处使用的合适类型,而不是BigDecimal?如果您尝试使用具有精确十进制值的数字(例如,货币),则BigDecimal是更合适的IMO类型。


40

我发现转换为二进制表示形式更容易解决此问题。

float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;

System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));

您可以看到,通过在末尾添加0将浮点数扩展为双精度数,但是0.27的双精度表示形式“更准确”,因此出现了问题。

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

25

这是由于的合同Float.toString(float)其中部分规定:

小数部分[…]必须打印多少位数?必须有至少一位数字来表示小数部分,并且除此数字外,还必须要有多少 个数字才能唯一地将参数值与相邻的float类型的值区分开来。也就是说,假设x是此方法针对有限的非零参数f生成的十进制表示形式所表示的精确数学值。那么f必须是最接近x的浮点值;或者,如果两个浮点值相等地接近于x,则f必须是其中之一,并且f有效位的最低有效位必须为0。


13

我今天遇到了这个问题,无法使用对BigDecimal的重构,因为该项目确实很大。但是我发现解决方案使用

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

这可行。

请注意,调用result.doubleValue()返回5623.22998046875

但是调用doubleResult.doubleValue()会正确返回5623.23

但是我不确定它是否是正确的解决方案。


1
为我工作。也非常快。不知道为什么没有将此标记为答案。
Sameer

这是我使用的方法,您不需要新的Float部件...只需使用基元创建FloatingDecimal。它会被自动装箱...并且也可以快速运行...有一个缺点; FloatingDecimal是不可变的,因此您需要为每个float都创建一个...想像一下O(1e10)的计算代码! !! 当然,BigDecimal具有相同的缺点……
Mostafa Zeinali 2014年

6
此解决方案的缺点是sun.misc.FloatingDecimal是JVM的内部类,并且其构造函数的签名在Java 1.8中已更改。在现实生活中,任何人都不应使用内部类。
Igor Bljahhin 2015年

7

使用a BigDecimal代替float/ double。有很多数字不能表示为二进制浮点数(例如0.1)。因此,您必须始终将结果四舍五入到已知精度或使用BigDecimal

有关更多信息,请参见http://en.wikipedia.org/wiki/Floating_point


假设您的缓存中有1000万浮动交易价格,您是否仍考虑使用BigDecimal并实例化唯一的,例如约600万个对象(以折扣flyweight / immutable)或更多呢?
Sendi_t 2015年

1
@Sendi_t请不要使用评论提出复杂的问题:-)
亚伦·迪古拉

感谢您的关注!..我们现在使用浮点运算,就像十年前达成的协议一样-我正尝试在后续工作中看到您对此情况的看法-。谢谢!
Sendi_t 2015年

@Sendi_t误会。我无法用512个字符回答您的问题。请提出一个正确的问题,并给我发送链接。
亚伦·迪古拉

1
@GKFX在计算机上处​​理小数时,没有“一刀切”的功能。但是由于大多数人不了解,所以我向他们指出,BigDecimal因为那样会捕获大多数常见错误。如果事情太慢,他们需要学习更多并找到优化问题的方法。过早的优化是万恶之源-DE Knuth。
亚伦·迪古拉

7

我发现以下解决方案:

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

如果使用floatdouble而不是FloatDouble,请使用以下命令:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}

1

从本质上讲,浮标是不精确的,并且总是具有整齐的四舍五入“问题”。如果精度很重要,那么您可以考虑将应用程序重构为使用Decimal或BigDecimal。

是的,由于对处理器的支持,浮点运算在计算上比十进制更快。但是,您想要快速还是准确?


1
小数运算也不精确。(例如1/3 * 3 == 0.9999999999999999999999999999)当然,它代表货币之类的精确十进制数更好,但是对于物理测量而言,它没有优势。
dan04 2010年

2
但是1 == 0.9999999999999999999999999999 :)
伊曼纽尔·布尔格

0

有关信息,请参见Joshua Bloch的Effective Java 2nd Edition的条款48-当需要精确值时避免浮点和双精度。这本书挤满了好东西,绝对值得一看。


0

这样行吗?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

0

一个有效的简单解决方案是从float的字符串表示形式解析double:

double val = Double.valueOf(String.valueOf(yourFloat));

效率不高,但可以!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.