数値の操作
数値の変換
数値と文字列の変換
Java
jshell> Integer.toString(12345)
$1 ==> "12345"
jshell> String.valueOf(12345)
$2 ==> "12345"
jshell> String.valueOf(123.45)
$3 ==> "123.45"
jshell> Integer.parseInt("12345")
$4 ==> 12345
jshell> Double.parseDouble("123.45")
$5 ==> 123.45
各ラッパークラスに用意されている toString()
または String.valueOf()
で文字列に変換する。文字列から数値へは parseXXX()
を使用する。
Scala
scala> 123.45.toString
res20: String = 123.45
scala> "12345".toInt
res21: Int = 12345
scala> "123.45".toDouble
res22: Double = 123.45
数値型、文字列型に定義されている toString
, toInt
等を使用する。
Julia
julia> string(123.45)
"123.45"
julia> parse(Int32, "12345")
12345
julia> parse(Float64, "123.45")
123.45
標準関数の string()
, parse()
を使用する。
数値に変換できない文字列の対応
Java
jshell> int x = 0;
x ==> 0
jshell> try {
...> x = Integer.parseInt("123456789xyz");
...> } catch(NumberFormatException ex){
...> // 変換できなかった場合の処理
...> System.err.println(ex);
...> }
java.lang.NumberFormatException: For input string: "123456789xyz"
変換処理を try-catch
で囲み失敗したときの処理を catch
ブロックに記述する。
Julia
julia> x = try parse(Int32, "foo") catch "error" end
"error"
julia> x = tryparse(Int32, "foo")
Nullable{Int32}()
julia> isnull(x)
true
tryparse()
を使って null をチェックするか、変換処理を try-catch
で囲み変換に失敗したときの処理を catch
ブロックに記述する。
Scala
scala> import scala.util.{Try,Success,Failure}
import scala.util.{Try,Success,Failure}
scala> val x = Try("foo".toInt).getOrElse(-1)
x: Int = -1
scala> Try("foo".toInt) match {
| case Success(x) => println(x) /* 成功時の処理 */
| case Failure(ex) => println(ex) /* 失敗時の処理 */
| }
java.lang.NumberFormatException: For input string: "foo"
Java と同様に try-catch
を使うことも可能だが Tryを使うのが一般的。getOrElse()
で失敗時にデフォルト値を返すだけでなく、match
で成功時の処理と失敗時の処理を記述したり、成功したかだけを判断する isSuccess
など記述のバリエーションが豊富。
数値と2進数、8進数、16進数、任意進数の変換
Java
jshell> Integer.toBinaryString(12345)
$13 ==> "11000000111001"
jshell> Integer.toOctalString(12345)
$14 ==> "30071"
jshell> Integer.toHexString(12345)
$15 ==> "3039"
jshell> Integer.toString(12345, 7)
$10 ==> "50664"
jshell> Character.MAX_RADIX
$11 ==> 36
jshell> for(int i=0; i<Character.MAX_RADIX; i++) System.out.print(Integer.toString(i, Character.MAX_RADIX))
0123456789abcdefghijklmnopqrstuvwxyz
Java は Integerクラスの toXxxString()
メソッドを使用して各進数の文字列へ変換を行う。toString()
に指定できる基数の最大値は Character.MAX_RADIX
まで。
jshell> Integer.parseInt("50664", 7)
$8 ==> 12345
jshell> Integer.parseInt("9ix", Character.MAX_RADIX)
$9 ==> 12345
文字列から整数への変換は基数指定の Integer.parseInt()
を使用する。ただし変換可能な基数の最大値は Character.MAX_RADIX
まで。
Scala
scala> 12345.toBinaryString
res26: String = 11000000111001
scala> 12345.toHexString
res27: String = 3039
scala> 12345.toOctalString
res30: String = 30071
Scala は 2, 8, 16 進数であれば整数型に定義されている toXxxString
を使用することができる。それ以外はJava と同じ。
Julia
julia> bin(12345)
"11000000111001"
julia> hex(12345)
"3039"
julia> oct(12345)
"30071"
julia> base(7, 12345)
"50664"
julia> for i in 0:35 print(base(36, i)) end
0123456789abcdefghijklmnopqrstuvwxyz
julia> for i in 0:61 print(base(62, i)) end
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Julia は 2, 8, 16 進数にそれぞれ標準関数が用意されている。基数はアルファベットの大文字と小文字を区別することで 62 まで使用することができるが、基数が 36 を超えると 10-36 部分が大文字で扱われるようになる点に注意。
julia> parse(Int32, "11000000111001", 2)
12345
julia> parse(Int32, "3039", 16)
12345
julia> parse(Int32, "50664", 7)
12345
julia> parse(Int32, "3D7", 62)
12345
Julia は parse()
関数に基数を指定することで任意の基数を数値に変換することができる。基数に指定できる最大値は 62 まで。
数値をコンマ区切りの文字列に変換
Java
int x = 123456789;
String s1 = String.format("%,d", x); // "123,456,789"
import java.text.NumberFormat;
NumberFormat nf = NumberFormat.getInstance();
String s2 = nf.format(x); // "123,456,789"
Java の標準機能 Locale
により数値の区切りが3桁でない国や区切り文字がコンマでない国でも文化圏に合わせた動作を行う (例えばドイツは ,
の代わりに .
を使う)。
Scala
scala> val x = 123456789
x: Int = 123456789
scala> f"comma => $x%,d"
res2: String = comma => 123,456,789
scala> "comma => %,d".format(x)
res3: String = comma => 123,456,789
Scala は Java と同じ機能が使える他に f"..."
で文字列内に式と書式を埋め込む String Interpolation が使用できる。
Julia
julia> Pkg.add("Formatting")
INFO: Initializing package repository C:\Users\Takami Torao\.julia\v0.6
INFO: Cloning METADATA from https://github.com/JuliaLang/METADATA.jl
INFO: Cloning cache of Formatting from https://github.com/JuliaIO/Formatting.jl.git
INFO: Installing Formatting v0.3.0
INFO: Package database updated
julia> using Formatting
INFO: Precompiling module Formatting.
julia> s = sprintf1("%'d", x)
"123,456,789"
julia> s = format(x, commas=true)
"123,456,789"
Formatting
パッケージを使用することでコンマ区切りの書式を利用できる。Formatting.jl 参照。
数値を小数点以下の桁数を指定した文字列に変換
Java
jshell> String.format("%.3f", Math.PI)
$6 ==> "3.142"
jshell> import java.util.Locale
jshell> Locale.setDefault(Locale.GERMAN)
jshell> String.format("%.3f", Math.PI)
$9 ==> "3,142"
String
クラスの format()
を使用する。このメソッドは実行環境の言語に合わせた小数点を使用する (例えばドイツは .
の代わりに ,
を使う)。ほかに数値フォーマットを指定する DecimalFormatも使用できる。
Scala
scala> f"${math.Pi}%.3f"
res18: String = 3.142
scala> "%.3f".format(math.Pi)
res19: String = 3.142
Scala は Java と同じ機能が使えるが f"..."
で文字列内に式と書式を埋め込む String Interpolation を使用するのが一般的。
Julia
julia> Pkg.add("Formatting")
INFO: Package Formatting is already installed
julia> using Formatting
julia> x = Float64(pi)
3.141592653589793
julia> s = sprintf1("%.3f", x)
"3.142"
julia> format(x, precision=3)
"3.142"
Formatting
パッケージを使用することでコンマ区切りの書式を利用できる (Pkg.add()
は初回のみで良い)。Formatting.jl 参照。
高精度の数値から低精度の数値へ変換
Java
jshell> double f64 = Math.PI
f64 ==> 3.141592653589793
jshell> float f32 = f64
| エラー:
| 不適合な型: 精度が失われる可能性があるdoubleからfloatへの変換
| float f32 = f64;
| ^-^
jshell> float f32 = (float)f64
f32 ==> 3.1415927
jshell> long i64 = 1234567890123456789L
i64 ==> 1234567890123456789
jshell> int i32 = i64
| エラー:
| 不適合な型: 精度が失われる可能性があるlongからintへの変換
| int i32 = i64;
| ^-^
jshell> int i32 = (int)i64
i32 ==> 2112454933
低精度側の型 float
, int
などへ明示的なキャストをする。
Scala
scala> val f64:Double = math.Pi
f64: Double = 3.141592653589793
scala> val f32:Float = f64
<console>:12: error: type mismatch;
found : Double
required: Float
val f32:Float = f64
^
scala> val f32:Float = f64.toFloat
f32: Float = 3.1415927
scala> val i64:Long = 100
i64: Long = 100
scala> val i32:Int = i64.toInt
i32: Int = 100
Scala はJava のようなキャストがない代わりに明示的に Double.toFloat
や Long.toInt
メソッドを使用して変換する。
Julia
julia> f64 = pi
π = 3.1415926535897...
julia> f32 = Float32(f64)
3.1415927f0
julia> i64 = Int64(100)
100
julia> i32 = Int32(i64)
100
動的型付け言語のため変数に型宣言はない。関数に渡すために精度を下げる場合は標準の Float32()
や Int32()
などを使用する。
数値とバイト配列の変換
Java (Scala)
jshell> import java.nio.ByteBuffer
jshell> import java.nio.ByteOrder
jshell> byte[] b1 = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN).putLong(1234567890L).array()
b1 ==> byte[8] { 0, 0, 0, 0, 73, -106, 2, -46 }
jshell> long i1 = ByteBuffer.wrap(b1).order(ByteOrder.BIG_ENDIAN).getLong()
i1 ==> 1234567890
jshell> byte[] b2 = ByteBuffer.allocate(Double.BYTES).order(ByteOrder.LITTLE_ENDIAN).putDouble(1234.56789).array()
b2 ==> byte[8] { -25, -58, -12, -124, 69, 74, -109, 64 }
jshell> double f2 = ByteBuffer.wrap(b2).order(ByteOrder.LITTLE_ENDIAN).getDouble()
f2 ==> 1234.56789
型と同じバイト数の ByteBufferをメモリに割り当て値を put して配列を参照する。バイトオーダーは作成するバイト配列をどちらにしたいかで変更可能 (デフォルトは BE)。Scala も同様。
Julia
julia> out = IOBuffer(Int(Int64.size))
IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=8, ptr=1, mark=-1)
julia> write(out, 1234567890)
8
julia> b = out.data
8-element Array{UInt8,1}:0xd2 0x02 0x96 0x49 0x00 0x00 0x00 0x00
julia> in = IOBuffer(b)
IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=1, mark=-1)
julia> read(in, Int64)
1234567890
IOBufferに数値を write()
する。バイトオーダーは実行環境に依存するため BE, LE どちらかに固定したければ変換処理が必要。
julia> hex2bytes(num2hex(1234567890))
8-element Array{UInt8,1}: 0x00 0x00 0x00 0x00 0x49 0x96 0x02 0xd2
julia> hex2bytes(num2hex(1234.567890))
8-element Array{UInt8,1}: 0x40 0x93 0x4a 0x45 0x84 0xf4 0xc6 0xe7
処理効率は落ちるが一度16進数文字列に変換したあとにバイト配列へ変換する簡便な方法もある (逆の変換も可能)。この方法のバイトオーダーは BE となる。
バイトオーダーの変換
実行環境の CPU アーキテクチャによって整数が上位バイトを上位アドレスに持つか (LE) 下位アドレスに持つか (BE) が決まっている。この並び順のことをバイトオーダー (byte order) と呼ぶ。異なるアーキテクチャのコンピュータ間でネットワークやファイルを介してバイナリデータをやりとりする場合、その仕様で決められたバイトオーダーを使わなければならない。ここではそのバイトオーダーの変換方法を記述する。なお BE = Big Endian, LE = Little Endian の意味。
Java (Scala)
jshell> import java.nio.ByteBuffer
jshell> import java.nio.ByteOrder
jshell> ByteBuffer.allocate(Long.BYTES)
.order(ByteOrder.BIG_ENDIAN)
.putLong(1234567890123456789L)
.flip()
.order(ByteOrder.LITTLE_ENDIAN)
.getLong()
$19 ==> 1549776473967043089
JavaVM はどのようなコンピュータ上で動いていても BE が標準。バイト配列への変換と同様に一度 BE で書き込んで flip して LE で読み出せばバイトオーダーが逆転される。
jshell> long flipByteOrder(long num){
long x = 0;
for(int i=0; i<Long.BYTES; i++){
long y = num & (0xFFL << (i*8));
int shift = (Long.BYTES-i-1-i)*8;
x |= (shift >= 0)? (y << shift): (y >> -shift);
}
return x;
}
| 次を変更しました: メソッド flipByteOrder(long)
jshell> flipByteOrder(1234567890123456789L)
$36 ==> 1549776473967043089
バイトオーダーの変換程度にヒープを使いたくなければ上記のようにビット演算を用いて記述してもよい。
Julia
julia> hton(1234567890123456789)
1549776473967043089
julia> htol(1234567890123456789)
1234567890123456789
Julia では Unix C/C++ で標準的な Berkeley Sockets と同じバイトオーダー変換用の関数が利用できる。
ホストバイトオーダーとは実行環境のバイトオーダーのことで、これが BE か LE かは実行環境に依存する (例えば Intel x86 なら LE, Sun SPARC なら BE)。ネットワークバイトオーダーは BE のこと。つまり hton()
は実行環境の整数値を BE に変換する (実行環境が BE なら何もしない)。同様に実行環境の整数値を LE に変換する場合は htol()
を使用する。
BE, LE からホストバイトオーダーに変換する場合は ntoh()
, ltoh()
関数を使用する。