Java 如何检查双精度数是否最多有 n 个小数位?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/264937/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check if a double has at most n decimal places?
提问by Turismo
Currently i have this method:
目前我有这个方法:
static boolean checkDecimalPlaces(double d, int decimalPlaces){
if (d==0) return true;
double multiplier = Math.pow(10, decimalPlaces);
double check = d * multiplier;
check = Math.round(check);
check = check/multiplier;
return (d==check);
}
But this method fails for checkDecmialPlaces(649632196443.4279, 4)
probably because I do base 10 math on a base 2 number.
但是这种方法checkDecmialPlaces(649632196443.4279, 4)
可能失败了,因为我在以 2 为底的数字上进行了 10 次数学运算。
So how can this check be done correctly?
那么如何才能正确地进行这项检查呢?
I thought of getting a string representation of the double value and then check that with a regexp - but that felt weird.
我想获得 double 值的字符串表示,然后用正则表达式检查它 - 但这感觉很奇怪。
EDIT:Thanks for all the answers. There are cases where I really get a double and for those cases I implemented the following:
编辑:感谢所有的答案。在某些情况下,我真的得到了一个双倍,对于这些情况,我实施了以下内容:
private static boolean checkDecimalPlaces(double d, int decimalPlaces) {
if (d == 0) return true;
final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1));
double multiplier = Math.pow(10, decimalPlaces);
double check = d * multiplier;
long checkLong = (long) Math.abs(check);
check = checkLong / multiplier;
double e = Math.abs(d - check);
return e < epsilon;
}
I changed the round
to a truncation. Seems that the computation done in round
increases the inaccuracy too much. At least in the failing testcase.
As some of you pointed out if I could get to the 'real' string input I should use BigDecimal
to check and so I have done:
我将其更改round
为截断。似乎完成的计算round
增加了太多的不准确性。至少在失败的测试用例中。
正如你们中的一些人指出的那样,我是否可以进入“真实”字符串输入,我应该用它BigDecimal
来检查,所以我已经完成了:
BigDecimal decimal = new BigDecimal(value);
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces);
return checkDecimal.scale() == 0;
The double
value I get comes from the Apache POI API that reads excel files. I did a few tests and found out that although the API returns double
values for numeric cells I can get a accurate representation when I immediately format that double
with the DecimalFormat
:
double
我得到的值来自读取 excel 文件的 Apache POI API。我做了一些测试,结果发现,虽然API返回double
的数字单元格的值,我可以得到一个精确的表示时,我立即格式化double
用DecimalFormat
:
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);
This also works for values that can't be represented exactly in binary format.
这也适用于不能以二进制格式精确表示的值。
采纳答案by David Schmitt
The test fails, because you have reached the accuracy of the binary floating point representation, which is approximately 16 digits with IEEE754 double precision. Multiplying by 649632196443.4279 by 10000 will truncate the binary representation, leading to errors when rounding and dividing afterwards, thereby invalidating the result of your function completely.
测试失败,因为您已达到二进制浮点表示的精度,该精度约为 16 位IEEE754 double precision。将 649632196443.4279 乘以 10000 将截断二进制表示,导致在四舍五入和之后的除法时出错,从而使您的函数结果完全无效。
For more details see http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
有关更多详细信息,请参阅http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
A better way would be to check whether the n+1
decimal places are below a certain threshold. If d - round(d)
is less than epsilon
(see limit), the decimal representation of d
has no significant decimal places. Similarly if (d - round(d)) * 10^n
is less than epsilon
, d can have at most n
significant places.
更好的方法是检查n+1
小数位是否低于某个阈值。如果d - round(d)
小于epsilon
(请参阅limit),则 的小数表示d
没有有效的小数位。类似地,如果(d - round(d)) * 10^n
小于epsilon
, d 最多可以有n
重要的地方。
Use Jon Skeet's DoubleConverter
to check for the cases where d
isn't accurate enough to hold the decimal places you are looking for.
使用乔恩斯基特的DoubleConverter
检查,其中的情况下d
是不准确的足以容纳你正在寻找的小数位。
回答by paxdiablo
As with all floating point arithmetic, you should not check for equality, but rather that the error (epsilon) is sufficiently small.
与所有浮点运算一样,您不应检查相等性,而是检查误差 (epsilon) 是否足够小。
If you replace:
如果更换:
return (d==check);
with something like
像
return (Math.abs(d-check) <= 0.0000001);
it should work. Obviously, the epsilon should be selected to be small enough compared with the number of decimals you're checking for.
它应该工作。显然,与您要检查的小数位数相比,应选择足够小的 epsilon。
回答by Ned Batchelder
The double
type is a binary floating point number. There are always apparent inaccuracies in dealing with them as if they were decimal floating point numbers. I don't know that you'll ever be able to write your function so that it works the way you want.
的double
类型是一个二进制浮点数。在处理它们时总是有明显的不准确之处,就好像它们是十进制浮点数一样。我不知道您是否能够编写函数使其按您想要的方式工作。
You will likely have to go back to the original source of the number (a string input perhaps) and keep the decimal representation if it is important to you.
如果它对您很重要,您可能必须回到数字的原始来源(可能是字符串输入)并保留十进制表示。
回答by Ken Gentle
If your goal is to represent a number with exactly nsignificant figures to the right of the decimal, BigDecimalis the class to use.
如果您的目标是表示小数点右侧恰好有n 个有效数字的数字,则可以使用BigDecimal类。
Immutable, arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).
不可变的、任意精度的有符号十进制数。BigDecimal 由一个任意精度的整数非标度值和一个 32 位整数标度组成。如果为零或正数,则刻度是小数点右侧的位数。如果为负数,则数字的未换算值乘以 10 的取反乘幂。因此,由 BigDecimal 表示的数字的值是 (unscaledValue × 10-scale)。
scale
can be set via setScale(int)
scale
可以通过setScale(int) 设置
回答by tvanfosson
I'm not sure that this is really doable in general. For example, how many decimal places does 1.0e-13
have? What if it resulted from some rounding error while doing arithmetic and really is just 0
in disguise? If on, the other hand you are asking if there are any non-zero digits in the first ndecimal places you can do something like:
我不确定这在一般情况下是否真的可行。例如,小数点1.0e-13
有多少位?如果它是在做算术时由于一些舍入错误而导致的并且真的只是0
伪装怎么办?如果打开,另一方面您会问前n个小数位是否有任何非零数字,您可以执行以下操作:
static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){
// take advantage of truncation, may need to use BigInt here
// depending on your range
double d_abs = Math.abs(d);
unsigned long d_i = d_abs;
unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces);
return e > 0;
}
回答by Glenn
If you can switch to BigDecimal, then as Ken G explains, that's what you should be using.
如果您可以切换到 BigDecimal,那么正如 Ken G 所解释的那样,这就是您应该使用的。
If not, then you have to deal with a host of issues as mentioned in the other answers. To me, you are dealing with a binary number (double) and asking a question about a decimal representation of that number; i.e., you are asking about a String. I think your intuition is correct.
如果没有,那么您必须处理其他答案中提到的许多问题。对我来说,您正在处理一个二进制数(double)并询问有关该数字的十进制表示的问题;即,您正在询问字符串。我认为你的直觉是正确的。
回答by Norm Wright
I think this is better Convert to string and interrogate the value for the exponent
我认为这更好转换为字符串并询问指数的值
public int calcBase10Exponet (Number increment)
{
//toSting of 0.0=0.0
//toSting of 1.0=1.0
//toSting of 10.0=10.0
//toSting of 100.0=100.0
//toSting of 1000.0=1000.0
//toSting of 10000.0=10000.0
//toSting of 100000.0=100000.0
//toSting of 1000000.0=1000000.0
//toSting of 1.0E7=1.0E7
//toSting of 1.0E8=1.0E8
//toSting of 1.0E9=1.0E9
//toSting of 1.0E10=1.0E10
//toSting of 1.0E11=1.0E11
//toSting of 0.1=0.1
//toSting of 0.01=0.01
//toSting of 0.0010=0.0010 <== need to trim off this extra zero
//toSting of 1.0E-4=1.0E-4
//toSting of 1.0E-5=1.0E-5
//toSting of 1.0E-6=1.0E-6
//toSting of 1.0E-7=1.0E-7
//toSting of 1.0E-8=1.0E-8
//toSting of 1.0E-9=1.0E-9
//toSting of 1.0E-10=1.0E-10
//toSting of 1.0E-11=1.0E-11
double dbl = increment.doubleValue ();
String str = Double.toString (dbl);
// System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str);
if (str.contains ("E"))
{
return Integer.parseInt (str.substring (str.indexOf ("E") + 1));
}
if (str.endsWith (".0"))
{
return str.length () - 3;
}
while (str.endsWith ("0"))
{
str = str.substring (0, str.length () - 1);
}
return - (str.length () - str.indexOf (".") - 1);
}
回答by Dt D
Maybe it will be helpful. Here is my method
也许它会有所帮助。这是我的方法
private int checkPrecisionOfDouble(Double atribute) {
String s = String.valueOf(atribute);
String[] split = s.split("\.");
return split[1].length();
}
or
或者
private boolean checkPrecisionOfDouble(Double atribute, int decimalPlaces) {
String s = String.valueOf(atribute);
String[] split = s.split("\.");
return split[1].length() == decimalPlaces;
}