Java 如何检查double是否最多有n个小数位?

Java 如何检查double是否最多有n个小数位?,java,floating-point,decimal,Java,Floating Point,Decimal,目前我有以下方法: 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/mu

目前我有以下方法:

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);      
}
但是这个方法对于checkDecmialPlaces(649632196443.4279,4)来说失败了,可能是因为我是以2为基数来计算10的

那么,如何正确地进行这项检查呢

我想得到一个double值的字符串表示,然后用regexp检查它——但这感觉很奇怪

编辑: 谢谢你的回答。有些情况下,我真的得到了双重奖励,对于这些情况,我实施了以下措施:

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;
}
我得到的
double
值来自读取excel文件的ApachePOIAPI。我做了一些测试,发现尽管API返回数值单元格的
double
值,但当我立即用
十进制格式设置
double
的格式时,我可以得到准确的表示:

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);

这也适用于不能以二进制格式精确表示的值。

与所有浮点运算一样,不应检查是否相等,而应检查错误(ε)是否足够小

如果更换:

return (d==check);
有点像

return (Math.abs(d-check) <= 0.0000001);

return(Math.abs(d-check)double
类型是一个二进制浮点数。如果将它们当作十进制浮点数来处理,则总是存在明显的不精确性。我不知道您是否能够编写函数以使其按您希望的方式工作


如果对您很重要,您可能必须返回数字的原始来源(可能是字符串输入),并保留十进制表示。

如果您的目标是在小数点右侧精确地表示一个数字,则要使用的类是

不可变、任意精度签名 十进制数。大十进制数由 任意精度整数 无标度值和32位整数 刻度。如果为零或正,则刻度为 是右边的位数 小数点的。如果为负数,则 数字的未标度值为 乘以10,等于 量表的否定。量表的值 由 因此,BigDecimal是(非标度值 ×10标度)


scale
可以通过

设置。我不确定这在一般情况下是否可行。例如,
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;
   }

测试失败,因为您已达到二进制浮点表示法的精度,约为16位。将649632196443.4279乘以10000将截断二进制表示法,导致随后的舍入和除法错误,从而使函数结果完全无效

有关更多详细信息,请参阅

更好的方法是检查
n+1
小数位是否低于某个阈值。如果
d-四舍五入(d)
小于(请参阅),
d
的小数位表示形式没有有效的小数位。同样,如果
(d-四舍五入(d))*10^n
小于
epsilon
,d最多可以有
n
个有效位置


使用“s”检查
d
不够精确,无法保留您要查找的小数位数的情况。

如果您可以切换到BigDecimal,那么正如Ken G所解释的,这就是您应该使用的

如果不是,那么你必须处理其他答案中提到的一系列问题。对我来说,你处理的是一个二进制数(double)并问一个关于该数字的十进制表示的问题;也就是说,你问的是一个字符串。我认为你的直觉是正确的。

我认为这样更好 转换为字符串并查询指数的值

 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);
 }
public int calcBase10Exponet(数字增量)
{
//抛掷0.0=0.0
//抛掷1.0=1.0
//抛掷10.0=10.0
//抛掷100.0=100.0
//抛掷1000.0=1000.0
//抛掷10000.0=10000.0
//抛掷100000.0=100000.0
//抛掷1000000.0=1000000.0
//抛掷1.0E7=1.0E7
//抛掷1.0E8=1.0E8
//抛掷1.0E9=1.0E9
//抛掷1.0E10=1.0E10
//抛掷1.0E11=1.0E11
//抛掷0.1=0.1
//抛掷0.01=0.01

//投掷0.0010=0.0010也许会有帮助。这是我的方法

private int checkPrecisionOfDouble(Double atribute) {
    String s = String.valueOf(atribute);
    String[] split = s.split("\\.");
    return split[1].length();
}


当您这样做时,最大的问题是溢出:check=d*multiplier;这不适用于大数字;IEE 754 64位double的精度仅为18位左右,因此,如果您进入的数字乘以1000将导致溢出(约10^304),您可以确定表示中有零位小数。“正确”这里没有什么意义。你的随机十进制数没有精确的二进制表示。你正在寻找一个“足够接近”的十进制数所以人们喜欢十进制版本的二进制值。有多接近才算足够?实际上你的答案帮助了我,但现在它是有缺陷的,因为你必须在截断它之前乘以
d
,以避免丢失有效数字-参见我对问题的编辑我还查看了Jon在回答中发布的Double Converter,它似乎是to做的和BigDecimal差不多。我想知道他为什么删除他的答案。
private int checkPrecisionOfDouble(Double atribute) {
    String s = String.valueOf(atribute);
    String[] split = s.split("\\.");
    return split[1].length();
}
private boolean checkPrecisionOfDouble(Double atribute, int decimalPlaces) {
    String s = String.valueOf(atribute);
    String[] split = s.split("\\.");

    return split[1].length() == decimalPlaces;
}