2011-05-19 51 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