我正在研究一种将字符串转换为适当的Number
类型的方法,具体取决于数字的格式。如果数字看起来是一个浮点值,那么我需要返回我可以使用的最小类型而不牺牲精度(Float
,Double
或BigDecimal
)。
基于How many significant digits have floats and doubles in java?(和其他资源),我已经学会了比Float
值有23位的尾数。基于此,我使用以下方法返回给定值的位长度:
private static int getBitLengthOfSignificand(String integerPart,
String fractionalPart) {
return new BigInteger(integerPart + fractionalPart).bitLength();
}
如果此测试的结果低于24,我将返回Float
。如果低于53,我会返回Double
,否则返回BigDecimal
。
但是,当我考虑Float.MAX_VALUE
3.4028235E38
时,我对结果感到困惑。根据我的方法(有integerPart = 3
和fractionalPart = 4028235
,有效数字的位长是26。这会触发我的方法返回Double
,当Float
显然就足够了。< / p>
有人可以突出我的思考或实施中的缺陷吗?我的另一个想法是将字符串转换为BigDecimal
并使用floatValue()
和doubleValue()
缩小,测试溢出(由无限值表示)。但这会失去精确度,因此对我来说并不合适。
答案 0 :(得分:1)
有效数据以二进制形式存储,只有当你不让它混淆时,你才可以把它想象成十进制表示中的数字。
指数是二进制指数,它不代表乘以10的幂乘以2的幂。因此,您使用的数字中的E38
只是一个便利:真正的有效数字是二进制的,应该乘以2的幂来获得实际数字。两个权力和十个权力并不相同,因此“3.4028235”不是真正的意义。
Float.MAX_VALUE
的实际有效位数为十六进制表示法,0x1.fffffe,其关联指数为127,表示Float.MAX_VALUE
实际为0x1.fffffe * 2 127 。
查看十进制表示以选择二进制浮点类型来放置值,正如您尝试的那样,并不起作用。首先,一个肯定从float
恢复的十进制数字的数量不同于为了区分float
与其邻居(6和9)而需要写入的十进制数字的数量。分别)。您选择编写“3.4028235E38”但您可以编写3.40282E38,对于您的算法,它看起来更容易表示,当它不是真的时候。当人们写道“3.4028235E38”是float
类型的最大有限值时,它们意味着如果你将这个十进制数舍入到float
,你将到达最大的浮点数。如果您将“3.4028235E38”解析为双精度数字,它甚至不会等于Float.MAX_VALUE
。
换句话说:另一种写Float.MAX_VALUE
的方式是3.4028234663852885981170418348451692544E38
。它仍然可以表示为float
(它表示与3.4028235E38
完全相同的值)。看起来它有很多位数,因为它们是小数出现的十进制数字,实际上这个数字在内部用二进制指数表示。
(顺便说一下,你的方法不会检查指数是否在范围内来表示所选类型中的数字,这是一种类型能够表示字符串中数字的另一个条件。)
答案 1 :(得分:0)
我会根据实际值与最近的float
之间的差异来工作。 BigDecimal
可以精确地存储任何有限长度的小数部分并对其进行算术运算:
将String
转换为最近的float
x
。如果x
是无限的,但该值具有有限double
表示,请使用该表达式。
将String
完全转换为BigDecimal
y
。
如果y
为零,请使用float
,它可以完全代表零。
如果没有,请将float
x
转换为BigDecimal
,z
。
在BigDecimal
中计算合理的小数位数,(y-z)/z
的绝对值。这是由于使用float
导致的相对舍入误差。如果它足够小,您选择的值少于某个值,请使用float
。如果没有,请使用double
。
如果你真的不想牺牲精确度,那就简单多了。转换为float
和double
。比较他们的平等。比较将在double
中完成。如果他们比较相等,请使用float
。如果没有,请使用double
。