はじめに
最近、進数変換が実装として必要な場面がありました。
10進数からN進数への変換。
みんなどうしてるの?
ということで解決策を考えてみます。
32進数と62進数
今回は10進数から32進数と62進数の変換です。
10進数は日常的に使われていますね。
0123456789で表現されます。9の次は位が上がって10と表現します
32進数は
0123456789
ABCDEFGHIJ
KLMNOPQRST
UV
です。Base32とはことなるようです。
0~Vまでの32進数表記はbase32hexと呼びます。RFC 4648で規格として決まっているようです。
62進数は
0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdedghijklmnopqrstuvwxyz
です。
オレオレ実装は避けたい
10進数の値をひたすら基数で割った余りと商に分解し、余りを連結してリバースすれば0から基数の間の値になります。
それを進数のとりうる値にマッピングすれば、進数変換が出来るのですが。
こんな感じです。
/** * 10進数をn進数に変換する * * @param radix 基数 * @param value 10進数のint値 * @return 進数変換した文字列 */ public String convertTenRadixTo(int radix, int value) { List<Integer> list = new ArrayList<>(); if (radix == 0 || value == 0) { return "0"; } // ひたすら基数割って余りをリストにadd(0〜Nの値を持つリストになる) while (value > 0) { // 基数での余り list.add(value % radix); // 元の値を基数で割った商で上書き value = (value / radix); } StringBuilder builder = new StringBuilder(); // 1〜Nまでの値を進数の値にマッピング for (Integer number : list) { builder.append(RADIX_MAP.get(String.valueOf(number))); } // 順序が逆になっているのでリバースしてtoString return builder.reverse().toString(); } /** * 手抜きのMap */ private static final Map<String, String> RADIX_MAP = new HashMap<String, String>() { { put("0", "0"); put("1", "1"); put("2", "2"); put("3", "3"); put("4", "4"); put("5", "5"); put("6", "6"); put("7", "7"); put("8", "8"); put("9", "9"); put("10", "a"); put("11", "b"); put("12", "c"); put("13", "d"); put("14", "e"); put("15", "f"); put("16", "g"); put("17", "h"); put("18", "i"); put("19", "j"); put("20", "k"); put("21", "l"); put("22", "m"); put("23", "n"); put("24", "o"); put("25", "p"); put("26", "q"); put("27", "r"); put("28", "s"); put("29", "t"); put("30", "u"); put("31", "v"); put("32", "w"); put("33", "x"); put("34", "y"); put("35", "z"); } };
…………。Javaでオレオレ実装するのはなるべく避けたい。
IntegerとBigInteger
そんなあなたにおすすめなのがjava.lang.Integerです。モダンなJavaのクラスですね。
Integer#parseInt(String s, int radix)や
Integer#toString(int i, int radix) って感じで進数変換が出来ちゃいます!
その他、2進数への8進数や16進数への変換メソッドが用意されています。
Integer#toBinaryString(int i)・・・2進数
Integer#toOctalString(int i)・・・8進数
Integer#toHexString(int i)・・・16進数
Integer#decodeメソッドでも8進数や16進数への変換が出来ます。
この方の記事が使い方として分かりやすいですね。
n進数の表現 - chihiro on Qiita
でもIntegerの精度を越える範囲の値を扱う場合はどうするんだ!となります。
そういうときはjava.math.BigIntegerです!モダンなJavaのクラスですね。
BigIntegerインスタンスを生成してtoString(int radix)メソッドを使えば進数変換できちゃいます!
BigIntegerの精度って?
では、BigIntegerはどのぐらいまでの値を持てるのでしょうか。
調べてみるとStack Overflowにたどりつきました。
Is there an upper bound to BigInteger? [duplicate]
どうやらBigIntegerの内部ではintの配列で値を持っているらしく、それが持てる値の範囲になる、と。
つまり(2^32)^Integer.MAX_VALUEらしいです。
ほぼこれでイケるじゃん!やったね!
BigIntegerにも限界がある
ところがBigIntegerにも限界があるっぽいのです。話が一転しちゃいました。
BigIntegerのtoString(int radix)メソッドは36進数までしか対応できない!なんで?
これはCharcterのMIN_RADIX ~MAX_RADIXの範囲を越えてしまうと変換が出来ないかららしいです。
BigIntegerのjavadocに書いてあります。
これはIntegerに関しても同様のようです。
62進数はオレオレ実装するしかない??
ということで、結局62進数はオレオレ実装するしかない。ということに…(たぶん)
調べてみるといろんな人がオレオレ実装してるのが出てきます。
その他
こぼれ話ですがBase64に関してはJava SE 8からBase64クラスが導入されました。
Base64.getEncoder().encodeToString(byte[] value)とかが出来ます。
komiya-atsushi/Base64Demo.java
あと、BigDecimalのとりうる値は理論上 2793926648桁らしいです?
BigDecimalといえば、きどさんという方のブログによく行く着きます。
java.math.BigDecimalの最大桁数(理論値)- きどたかのブログ
まとめ
ということで、まとめです。
・36進数まではBigIntegerでだいたいイケる
・37進数以降オレオレ実装するしかなさそう
(なんか良いライブラリ無いかな?)
結論がオレオレ実装しましょう、ってやだなぁ〜。
@yy_yank RFCで規定されてないから、Base62って言ってる時点でオレオレなんですよー。
— CEROMETAL (@cero_t) October 5, 2015
なるほど〜。
0 件のコメント:
コメントを投稿