2011-05-19 31 views
12

我曾尝试通过下面的方法来分离5.6(例如)的最佳方式:什么是分离的双成两个部分“整数和分数”在java中

private static double[] method(double d) 
{ 
    int integerPart = 0; 
    double fractionPart = 0.0; 
    integerPart = (int) d; 
    fractionPart = d - integerPart; 
    return new double[]{integerPart, fractionPart}; 
} 

但我得到的是:

[0] = 5.0 
[1] = 0.5999999999999996 

你有没有关于这样做的建议,而不将数字转换为字符串?

+0

用另一个建议更新了我的答案:-) – aioobe 2011-05-19 21:31:43

回答

12

使用BigDecimal来做同样的计算。 (使用双打由于其表示有精确性问题)。

  • new BigDecimal(String.valueOf(yourDouble))构建它(这仍然是通过串去,但是部分不通过字符串操作分开)
  • 使用bd.subtract(new BigDecimal(bd.intValue())确定分数
+0

为什么不使用新的BigDecimal(double val)构造? – Swati 2011-05-19 18:44:17

+0

因为: @Swati:常规:000>新的BigDecimal(5.6) - 新的BigDecimal(5.0) ===> 0.5999999999999996447286321199499070644378662109375(这不是减法的故障,当5.6被转换为一个BigDecimal它引入) – 2011-05-19 18:44:17

+1

@ Swati:double是基数2,十进制是基数10.小数部分是因为5.6的十进制不能用二进制精确表示。如果用双精度构造小数,那么已经引入了不准确性。 – 2011-05-19 18:45:42

5

这里是基于BigDecimal另一种解决方案(这不通过String)。

private static double[] method(double d) { 
    BigDecimal bd = new BigDecimal(d); 
    return new double[] { bd.intValue(), 
          bd.remainder(BigDecimal.ONE).doubleValue() }; 
} 

正如你会注意到,你还是不会得到公正0.6为小数部分的输出。 (你甚至不能存储0.6double!)这是由于数学,实数,5.6实际上不是由双完全一样5.6但5.599999代表的事实...


你也可以做

private static double[] method(double d) { 
    BigDecimal bd = BigDecimal.valueOf(d); 
    return new double[] { bd.intValue(), 
          bd.remainder(BigDecimal.ONE).doubleValue() }; 
} 

其实际收益率[5.0, 0.6]

但是,BigDecimal.valueOf在大多数JDK(内部)中是通过调用Double.toString实现的。但至少字符串相关的东西不会弄乱你的代码 :-)


好后续问题的评论:

如果被表示为5.599999999 .. ,那么为什么Double.toString(5.6)给出确切"5.6"

Double.toString方法实际上是非常复杂。从documentation of Double.toString

[...]

多少位必须被打印的米或一个小数部分?必须至少有一位数字来表示小数部分,并且除此之外还需要多少位数,但只需要多少位数来唯一地区分参数值和类型为double的相邻值。也就是说,假设x是由该方法产生的十进制表示形式对于有限非零参数d所表示的精确数学值。那么d必须是最接近x的double值;或者如果两个双值同样接近x,那么d必须它们中的一个和d的有效数字的最低位显著必须为0。

[...]

为代码获得字符"5.6"归结为FloatingDecimal.getChars

private int getChars(char[] result) { 
    assert nDigits <= 19 : nDigits; // generous bound on size of nDigits 
    int i = 0; 
    if (isNegative) { result[0] = '-'; i = 1; } 
    if (isExceptional) { 
     System.arraycopy(digits, 0, result, i, nDigits); 
     i += nDigits; 
    } else { 
     if (decExponent > 0 && decExponent < 8) { 
      // print digits.digits. 
      int charLength = Math.min(nDigits, decExponent); 
      System.arraycopy(digits, 0, result, i, charLength); 
      i += charLength; 
      if (charLength < decExponent) { 
       charLength = decExponent-charLength; 
       System.arraycopy(zero, 0, result, i, charLength); 
       i += charLength; 
       result[i++] = '.'; 
       result[i++] = '0'; 
      } else { 
       result[i++] = '.'; 
       if (charLength < nDigits) { 
        int t = nDigits - charLength; 
        System.arraycopy(digits, charLength, result, i, t); 
        i += t; 
       } else { 
        result[i++] = '0'; 
       } 
      } 
     } else if (decExponent <=0 && decExponent > -3) { 
      result[i++] = '0'; 
      result[i++] = '.'; 
      if (decExponent != 0) { 
       System.arraycopy(zero, 0, result, i, -decExponent); 
       i -= decExponent; 
      } 
      System.arraycopy(digits, 0, result, i, nDigits); 
      i += nDigits; 
     } else { 
      result[i++] = digits[0]; 
      result[i++] = '.'; 
      if (nDigits > 1) { 
       System.arraycopy(digits, 1, result, i, nDigits-1); 
       i += nDigits-1; 
      } else { 
       result[i++] = '0'; 
      } 
      result[i++] = 'E'; 
      int e; 
      if (decExponent <= 0) { 
       result[i++] = '-'; 
       e = -decExponent+1; 
      } else { 
       e = decExponent-1; 
      } 
      // decExponent has 1, 2, or 3, digits 
      if (e <= 9) { 
       result[i++] = (char)(e+'0'); 
      } else if (e <= 99) { 
       result[i++] = (char)(e/10 +'0'); 
       result[i++] = (char)(e%10 + '0'); 
      } else { 
       result[i++] = (char)(e/100+'0'); 
       e %= 100; 
       result[i++] = (char)(e/10+'0'); 
       result[i++] = (char)(e%10 + '0'); 
      } 
     } 
    } 
    return i; 
} 
+0

如果它表示为'5.599999999 ...',那么为什么'System.out.println(Double.toString(5.6));'完全给出''5.6' – 2011-05-19 18:49:06

+0

现在这是一个很好的问题。 Double.toString(5。6)'*非常复杂*。看看[文档](http://download.oracle.com/javase/6/docs/api/java/lang/Double.html#toString%28double%29)。 (宽松地说:它不会*尝试打印双精度值,但最接近表示值的最简单值比任何其他值都要好)。 – aioobe 2011-05-19 18:51:13

+0

“”+ d被转换为String.valueOf(d )''又调用'Double.toString(..)'(或者编译器直接转换为D.toString()) – Bozho 2011-05-19 18:57:42

1

要看看到底是怎么回事,看看这些数字的二进制表示:

double d = 5.6; 
System.err.printf("%016x%n", Double.doubleToLongBits(d)); 
double[] parts = method(d); 
System.err.printf("%016x %016x%n", 
        Double.doubleToLongBits(parts[0]), 
        Double.doubleToLongBits(parts[1])); 

输出:

4016666666666666 
4014000000000000 3fe3333333333330 

5.6 1.4 * 2 2 ,但0.6 1.2 * 2 -1。由于它具有较低的指数,因此归一化会导致尾数向左移位三位。事实上,经常性术语(..66666..)最初是7/5部分的近似值已被遗忘,缺失的位被替换为零。

给定原始值double作为输入到您的方法的值,无法避免这种情况。为了保持准确的值,你需要使用一个精确表示所需值的格式,例如来自Apache commons-math的Fraction。 (对于d=5.6这个具体例子的BigDecimal也将能够完全代表它,但也有其他的数字不能代表准确,如4/3)

0

可怜人的解决方案(使用字符串)

static double[] sp(double d) { 
     String str = String.format(Locale.US, "%f", d); 
     int i = str.indexOf('.'); 
     return new double[] { 
      Double.parseDouble(str.substring(0, i)), 
      Double.parseDouble(str.substring(i)) 
     }; 
    } 

(语言环境,所以我们真的得到了小数点

-1

字符串doubleAsString = Double.toString(123.456);

String beforeDecimal = doubleAsString.substring(0,doubleAsString.indexOf(“。”)); // 123

String afterDecimal = doubleAsString.substring(doubleAsString.indexOf(“。”)+ 1); // 456

+0

为什么你回答一个3岁的问题?一个好的答案需要更多的解释。 – Dinistro 2014-10-22 06:53:28