SSブログ

double型の値を文字列に変換 [プログラミング]

String a;
String b;
String c;

(略)

String s = a + b + c;

Javaで、こんなコードがあったとします。
文字列同士の加算は文字列の連結として解釈されます。

Java5では、文字列の連結に関して最適化が行われていて、

String s = a + b + c;

String s = (new StringBuilder()).append(a).append(b).append(c).toString();

と変換されてコンパイルされます。

この変換で速くなるのかどうかはVMの実装次第の様な気がしなくもないんですが…。


さて、

いま double型の変数があり、これを文字列に変換したいのです。

一番手っ取り早い方法がコレでしょう。

double d;

(略)

String s = "" + d;

このコードJava5では、こうなります。

String s = (new StringBuilder()).append("").append(d).toString();

.append("")は要らないってば。
もう少し賢く最適化してほしいです。

というか、多少面倒でも、こう↓書けと言う事なんだろうか…。

String s = Double.toString(d);

"" + dでも、Double.toString(d)でも良いんですが、double型の値の桁が大きくなったり小さくなったりすると、浮動小数点表示形式で変換されます。

100000000は「1.0E8」になります。(意味は、1.0×10の8乗)
0.0001なら「1.0E-4」です。

でも100000000くらい普通に「100000000」として欲しいのです。

そこで、浮動小数点表示形式にはならない文字列変換を作ることにしました。

ちなみに、動作環境は Java 1.4なのでFormatterの使用は不可です。


BigDecimalクラスにソレっぽい変換メソッドがありました。

double d = 0.0001;

String s = new BigDecimal(d).toPlainString();

結果は、こうなりました。

0.000100000000000000004792173602385929598312941379845142364501953125

なんじゃこりゃ。

0.0001ってのは2進数では正確に表せなくって近い値で近似しているんですが、その近似値を正確に10進数に戻したので、こんな風になってしまいます。

これでは使えません。
っていうか、BigDecimal#toPlainString()はJava5で導入されたメソッドなので、どのみち不可なんですけど…。


その2

double d = 0.0001;

String s = BigDecimal.valueOf(d).toPlainString();

結果

0.00010

最後に余計な「0」が付いてますが、中々良さそうです。

って、
BigDecimal#toPlainString()は使えないんだってば。


こうなったら自分で作るしか!

方針は、とりあえずDouble#toString()で変換してみて浮動小数点表示形式だった普通の実数の表示形式に変換し直す。

public static String convDoubleToString(double d)
{
    String str = Double.toString(d);

    int e_pos = str.indexOf('E');
    if (e_pos == -1) {
        // Eを含まなければそのまま出力
        // Infinity,NaN 共にEは含んでないので問題なし。
        return str;
    }

    int e = Integer.parseInt(str.substring(e_pos + 1));    // 指数部の値

    str = str.substring(0, e_pos);        // 実数部

    int p_pos = str.indexOf('.');        // 小数点の位置

    String n;        // 整数部
    String m;        // 小数部

    if (p_pos == -1) {
        // 小数点がないなんて事は無いはずだが、念のため
        n = str;
        m = "";
    } else {
        n = str.substring(0, p_pos);
        m = str.substring(p_pos + 1);
    }

    // 指数部の値を反映
    if (e > 0) {
        StringBuffer n0 = new StringBuffer(n);
        if (m.length() < e) {
            n0.append(m);
            for (int i = 0; i < e - m.length(); i++)
                n0.append('0');
            m = "";
        } else {
            n0.append(m.substring(0, e));
            m = m.substring(e);
        }
        n = n0.toString();
    } else {
        StringBuffer m0 = new StringBuffer(m);
        if (n.length() < -e) {
            m0.insert(0, n);
            for (int i = 0; i < -e - n.length(); i++)
                m0.insert(0, '0');
            n = "";
        } else {
            m0.insert(0, n.substring(n.length() + e));
            n = n.substring(0, n.length() + e);
        }
        m = m0.toString();
    }

    // 整数部の先頭についている余分な0を取り除く
    int n_pos = 0;
    while (n_pos < n.length() && n.charAt(n_pos) == '0') {
        n_pos++;
    }
    n = n.substring(n_pos);

    // 小数部の末尾についている余分な0を取り除く
    int m_pos = m.length() - 1;
    while (m_pos >= 0 && m.charAt(m_pos) == '0') {
        m_pos--;
    }
    m = m.substring(0, m_pos + 1);

    // 整数部が無いと言う事は、「0.~」なので「0」にする。
    if (n.length() == 0)
        n = "0";

    // 小数部が無いと言う事は、結果は整数
    if (m.length() == 0)
        return n;

    return n + "." + m;
}
[車(セダン)] 今日の一冊
ふしぎの国のアリス (偕成社文庫 2063)

ふしぎの国のアリス (偕成社文庫 2063)

  • 作者: ルイス・キャロル
  • 出版社/メーカー: 偕成社
  • 発売日: 1979/01
  • メディア: 単行本

タグ:Java
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

ペグ・ソリテア おためし版陣地取り ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。