1. Overview
1.概述
The most natural choice for us to deal with numbers in Java is to use the various primitive types available. We can use float or double to represent fractional numbers.
在 Java 中处理数字时,我们最自然的选择是使用各种可用的基元类型。我们可以使用 float 或 double 来表示小数。
This can be sufficient in certain use cases, but in others, especially when accuracy and precision are a requirement, BigDecimal is a better choice. Sometimes, we might need to convert from a float value to a BigDecimal. We’ll explore various ways to do it in this article.
在某些使用情况下,这样做就足够了,但在其他情况下,尤其是对精确度和准确性有要求时,BigDecimal 是更好的选择。有时,我们可能需要将 float 值转换为 BigDecimal 值。我们将在本文中探讨实现转换的各种方法。
2. Brief Description of float and BigDecimal Types
2.简要说明 float 和 BigDecimal 类型
Before discussing converting from float to BigDecimal, let’s describe them briefly.
在讨论从 float 转换到 BigDecimal 之前,让我们先简单介绍一下它们。
2.1. float
2.1. float.
The primitive float is a 32-bit type used to represent floating point numbers, i.e., fractional numbers. The float implementation follows the IEEE 754 specification, a widespread standard. It has its Java class counterpart named Float. We also have a 64-bit version named double. We can call a float a single-precision type and double a double-precision type.
基元 float 是一种 32 位类型,用于表示浮点数,即小数。float 的实现遵循 IEEE 754 规范,这是一个广泛使用的标准。它的 Java 类对应名为 Float。我们还有一个 64 位版本,名为 double。我们可以将 float 称为单精度类型,将 double 称为双精度类型。
The internal implementation of floating point numbers in computers is inherently not accurate. Representing real numbers with exact precision would require infinite bits. We can easily see what problems can arise by running some simple tests:
计算机中浮点数的内部实现本身就不精确。要精确地表示实数,需要无限位。通过一些简单的测试,我们可以很容易地看出会出现哪些问题:
@Test
public void whenFloatComparedWithDifferentValues_thenCouldMatch() {
assertNotEquals(1.1f, 1.09f);
assertEquals(1.1f, 1.09999999f);
}
So, even though “1.1” and “1.09999999” are different numbers, they look the same when compared to each other in a Java program using float types.
因此,即使 “1.1 “和 “1.09999999 “是不同的数字,但在使用 float 类型的 Java 程序中相互比较时,它们看起来是一样的。
We can see what the internal representation of various float numbers is by using the online tool FloatConverter and testing it with the fraction “1.1” used in the above example. The internal value stored by the computer, according to the tool, is “1.10000002384185791015625”.
我们可以使用在线工具 FloatConverter 并用上述示例中使用的分数 “1.1 “进行测试,从而了解各种 float 数字的内部表示形式。根据该工具,计算机存储的内部值为 “1.10000002384185791015625”。
When we convert a fractional number from decimal to binary, we need a specific number of bits, which could be infinite for some fractions. In the computer, we have a limited number of bits available. If we convert back this finite representation to the decimal base again, we can get a result different from the original one.
当我们把小数从十进制转换成二进制时,需要特定的位数,对于某些小数来说,位数可能是无限的。在计算机中,我们可用的位数是有限的。如果我们将这个有限的表示法再次转换回十进制,就会得到与原来不同的结果。
We’ll see various conversion approaches from float to BigDecimal in the following sections, using the 1.10000002384185791015625f value as a consistent float example to convert to ease the discussion since we know it is accurately represented in IEEE 754 binary form.
在接下来的章节中,我们将看到从 float 到 BigDecimal 的各种转换方法,我们将使用 1.10000002384185791015625f 值作为一个一致的 float 转换示例,以方便讨论,因为我们知道它是以 IEEE 754 二进制形式准确表示的。
2.2. BigDecimal
2.2. 大十进制
The BigDecimal class is the recommended way to deal with fractional numbers when we require more accuracy and precision. For instance, this is often the case with financial applications.
BigDecimal 类是我们在需要更高精度和准确性时处理小数的推荐方法。例如,在金融应用中经常出现这种情况。
It has a so-called “unscaled” part, which is an integer with arbitrary precision (limited only by available memory), and a 32-bit integer part, named “scale”, representing the number of digits to the right of the decimal point.
它有一个所谓的 “未缩放 “部分,是一个任意精度的整数(仅受可用内存的限制),还有一个名为 “缩放 “的 32 位整数部分,代表小数点右边的位数。
BigDecimal provides methods to perform various operations on numbers and format conversions. A BigDecimal instance represents a single fractional number. We can use the toString method to obtain a String representation of an instance.
BigDecimal 提供了对数字进行各种操作和格式转换的方法。一个 BigDecimal 实例代表一个小数。我们可以使用 toString 方法获得实例的 String 表示。
3. How To Convert a float to BigDecimal
3.如何将 float 转换为 BigDecimal</em
BigDecimal has a number of constructors and methods to perform type conversions, including float to BigDecimal. We need to take care though, because of the limits of the floating point number representation implemented by the Java float type. We’ll see different ways of performing the conversion in the following sections.
BigDecimal 有许多构造函数和方法用于执行类型转换,包括将 float 转换为 BigDecimal。不过我们需要注意,因为 Java float 类型实现的浮点数表示法存在限制。我们将在下面的章节中看到执行转换的不同方法。
3.1. Using BigDecimal double Constructor with float Argument
3.1.使用带有 float 参数的 BigDecimal double 构造函数
One of the constructors of BigDecimal takes a double as a parameter. We can write a simple test to evaluate the result of the conversion when passing to it a float argument:
BigDecimal 的一个构造函数将 double 作为参数。我们可以编写一个简单的测试,在向它传递一个 float 参数时,评估转换的结果:
@Test
public void whenCreatedFromFloat_thenMatchesInternallyStoredValue() {
float floatToConvert = 1.10000002384185791015625f;
BigDecimal bdFromFloat = new BigDecimal(floatToConvert);
assertEquals("1.10000002384185791015625", bdFromFloat.toString());
}
We have used 1.10000002384185791015625f, which is the same value we encountered using the online tool, and we know it is accurately represented. We can see here that BigDecimal performs a conversion consistent with the IEEE 754 representation.
我们使用了 1.10000002384185791015625f,这与我们使用在线工具遇到的值相同,我们知道它的表示是准确的。我们可以看到,BigDecimal 执行的转换与 IEEE 754 表示法一致。
With the BigDecimal float constructor, in real scenarios with existing float values, we can at least be confident that the conversion is aligned with the internal floating-point representation. It’s up to the programmer to deal with the BigDecimal resulting values with caution.
使用 BigDecimal float 构造函数,在使用现有 float 值的实际场景中,我们至少可以确信转换与内部浮点表示一致。程序员应谨慎处理 BigDecimal 结果值。
3.2. Using BigDecimal Constructor with String Argument
3.2.使用带有 String 参数的 BigDecimal 构造函数
The most reliable way to initialize a BigDecimal with a fractional number is to use its constructor with a String parameter, passing an input value originally represented as a String object. Typical scenarios are those in which input numbers come from a user interface and are stored as String objects. Those numbers can be preserved in their original representation by using the constructor mentioned above:
使用小数初始化 BigDecimal 的最可靠方法是使用带有 String 参数的构造函数,传递最初表示为 String 对象的输入值。典型的应用场景是输入的数字来自用户界面并存储为 String 对象。通过使用上述构造函数,可以保留这些数字的原始表示形式:
@Test
public void whenCreatedFromString_thenPreservesTheOriginal() {
BigDecimal bdFromString = new BigDecimal("1.1");
assertEquals("1.1", bdFromString.toString());
}
In this article, though, we are covering float to BigDecimal conversion. We take a float value, use the toString method of the Float class to generate the String representation, and then pass it to the BigDecimal constructor:
在本文中,我们将介绍 float 到 BigDecimal 的转换。我们获取一个 float 值,使用 Float 类的 toString 方法生成 String 表示,然后将其传递给 BigDecimal 构造函数:
@Test
public void whenCreatedFromFloatConvertedToString_thenFloatInternalValueGetsTruncated() {
String floatValue = Float.toString(1.10000002384185791015625f);
BigDecimal bdFromString = new BigDecimal(floatValue);
assertEquals("1.1", floatValue);
assertEquals("1.1", bdFromString.toString());
}
In the above test, we can see that the Float toString method gives “1.1” when applied to 1.10000002384185791015625f, and the BigDecimal String constructor just preserves this intermediate String value.
在上述测试中,我们可以看到 Float toString 方法在应用于 1.10000002384185791015625f 时给出了 “1.1”,而 BigDecimal String 构造函数只是保留了这个中间 String 值。
Converting existing float values to BigDecimal by its String constructor is not reliable in terms of preserving the precision of their internal representation.
通过 String 构造函数将现有 float 值转换为 BigDecimal 在保持其内部表示精度方面并不可靠。
3.3. Using BigDecimal valueOf Method
3.3.使用 BigDecimal valueOf 方法
Besides its constructors, BigDecimal also has a valueOf static method to perform the conversion, with a double as the only argument. We can see from the BigDecimal source code that its implementation simply passes the result of the Double toString method to the String constructor :
除了构造函数外,BigDecimal 还有一个 valueOf 静态方法来执行转换,唯一的参数是 double。我们可以从 BigDecimal 源代码中看到,它的实现只是将 Double toString 方法的结果传递给 String 构造函数:
public static BigDecimal valueOf(double val) {
return new BigDecimal(Double.toString(val));
}
The Double toString execution has the effect of truncating 1.10000002384185791015625f to “1.100000023841858”:
Double toString 执行的效果是将 1.10000002384185791015625f 截断为 “1.100000023841858” :
@Test
public void whenDoubleConvertsFloatToString_thenFloatValueGetsTruncated() {
assertEquals("1.100000023841858", Double.toString(1.10000002384185791015625f));
}
Since the valueOf implementation is just Double.toString(value), we expect the same result by executing it:
由于 valueOf 的实现只是 Double.toString(value),因此我们期望执行它能得到相同的结果:
@Test
public void whenCreatedByValueOf_thenFloatValueGetsTruncated() {
assertEquals("1.100000023841858", BigDecimal.valueOf(1.10000002384185791015625f).toString());
}
We can conclude that the use of the valueOf method retains less precision than the constructor with a float argument.
我们可以得出结论,使用 valueOf 方法比使用 float 参数的构造函数保留的精度更低。
4. Conclusion
4.结论
In this article, we have described several ways of converting float values to BigDecimal. The BigDecimal constructor with a float argument has the best result in keeping the precision of an existing float value.
在本文中,我们介绍了将 float 值转换为 BigDecimal 的几种方法。带有 float 参数的 BigDecimal 构造函数在保持现有 float 值的精度方面效果最佳。
We can also conclude that if we want complete control of the initialization of BigDecimal with fractional numbers, we should provide values originally represented as String objects to the String constructor.
我们还可以得出这样的结论:如果我们希望完全控制 BigDecimal 的小数初始化,就应该向 String 构造函数提供最初表示为 String 对象的值。
The example code is available over on GitHub.
示例代码可在 GitHub 上获取。