LS-DYNA用の浮動小数点数をフォーマット

LS-DYNAのキーワードファイルを書きだすプログラムを書いたところ、浮動小数点数を固定文字数(10文字)にフォーマットする所でつまづいた。 具体的には

  • String.formatやDecimalFormatの動作が遅い(String.formatよりはDecimalFormatのほうが速いが)
  • 文字数を10文字に制限することが難しい(絶対値の大小比較を行なって桁数ごとに条件分岐をしても、四捨五入によって意図しない文字数(11文字)になる)

という問題にあたった。そこでdoubleから長さ10の文字列を力技で書きだすロジックを自作した。特徴は以下の通り。

  • 正の時は先頭にスペース、負の時は先頭に”-“を入れる
  • 10文字の制限の中で最も有効桁数が大きくなるようにフォーマット
  • 四捨五入はしない!
  • DecimalFormatの15倍高速(もう少し工夫はできるが)
private static String formatFloat10(double d){
    char[] c = new char[10];
    double a;

    if(d == 0.0){
        return " 0.0000000";
    }
    if(d < 0){
        c[0] = '-';
        a = -d;
    }else{
        c[0] = ' ';
        a = d;
    }
    int m = (int)Math.floor(Math.log10(a));

    if(m <= -100){
        for(int i = 1; i < 1 - m; i++) a *= 10;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 5; i++){
            /*c[i] = (char)('0' + Math.floor(a));
            a = (a - Math.floor(a)) * 10;*/
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[5] = 'e';
        c[6] = '-';
        c[7] = (char)('0' + (-m) * 0.01);
        c[8] = (char)('0' + (-m * 0.1) % 10);
        c[9] = (char)('0' + (-m) % 10);

    }else if(m <= -10){
        for(int i = 1; i < 1 - m; i++) a *= 10;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 6; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[6] = 'e';
        c[7] = '-';
        c[8] = (char)('0' + (-m) * 0.1);
        c[9] = (char)('0' + (-m) % 10);

    }else if(m <= -4){
        for(int i = 1; i < 1 - m; i++) a *= 10;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 7; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[7] = 'e';
        c[8] = '-';
        c[9] = (char)('0' - m);

    }else if(m <= -1){
        c[1] = '0';
        c[2] = '.';
        a *= 10;
        for(int i = 3; i < 10; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }

    }else if(m <= 6){
        for(int i = 1; i < m + 1; i++) a *= 0.1;
        for(int i = 1; i < 10; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
            if(i == m + 1){
                c[++i] = '.';
            }
        }
    }else if(m <= 9){
        for(int i = 1; i < m + 1; i++) a *= 0.1;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 8; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[8] = 'e';
        c[9] = (char)('0' + m);

    }else if(m <= 99){
        for(int i = 1; i < m + 1; i++) a *= 0.1;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 7; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[7] = 'e';
        c[8] = (char)('0' + m * 0.1);
        c[9] = (char)('0' + m % 10);

    }else{
        for(int i = 1; i < m + 1; i++) a *= 0.1;
        c[1] = (char)('0' + Math.floor(a));
        a = (a - Math.floor(a)) * 10;
        c[2] = '.';
        for(int i = 3; i < 6; i++){
            double b = Math.floor(a);
            c[i] = (char)('0' + b);
            a = (a - b) * 10.0;
        }
        c[6] = 'e';
        c[7] = (char)('0' + m * 0.01);
        c[8] = (char)('0' + (m * 0.1) % 10);
        c[9] = (char)('0' + m % 10);

    }

    return new String(c);
}