SwiftのDecimal(string:)がどれだけ使えるか試してみた
SwiftのDecimal型には、小数の精度が落ちないようにするために文字列からDecimalのインスタンスを生成するイニシャライザがあります。
僕が公開しているRPN Anywhereという電卓アプリではDecimal型を利用して計算しているのですが、コピペ機能を実装したくてどの程度文字列を整形してからDecimal型のイニシャライザに与えればいいか知りたかったので調べました。
結論
先に結論を言うと
- 扱えるのは一般的な有理数と指数表記(eを使った10のべき乗表記)のみ
- 不正な文字がある場合、それ以前の文字列だけで評価される
です。
特に、コンマがあるとそれ以降の数字が無視されるのは致命的で、コピペの値をそのまま渡すのには使えないね。
なので、ユーザーの入力からDecimal型を生成するには整形してからにしましょう。
実験
$ swift -v
Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
Swiftのバージョンは4.2.1。
Macのターミナル上でswiftコマンドを実行し、対話的なインターフェイスを使って実験しました。
自然数
> print( Decimal(string: "3")! )
3
問題なく扱えます。当たり前。
小数点
> print( Decimal(string: "3.14159")! )
3.14159
もちろん扱えます。
> print( Decimal(string: "3.141.59")! )
3.141
意味のわからない2個目の小数点は不正なので、それ以降は無視されます
指数表記
> print( Decimal(string: "3.14159e3")! )
3141.59
これも問題なし
指数表記(小数)
> print( Decimal(string: "3.14159e0.5")! )
3.14159
これはやってくれない
指数表記(数字がない)
> print( Decimal(string: "3.14159e")! )
3.14159
記載がない場合は指数部が0になるのかな?
まあ結果はeを書いてない時と同じだ。
指数表記(複数)
> print( Decimal(string: "3.14159e3e2")! )
3141.59
変な書き方だけど、と解釈できないわけじゃないので
だけど、この場合2個目のeは不正で、それ以降の文字列は無視される。
符号
> print( Decimal(string: "+1")! )
1
> print( Decimal(string: "-1")! )
-1
まあ使えるでしょうな
符号だけ
> print( Decimal(string: "+")! )
0
> print( Decimal(string: "-")! )
0
エラーにならず0になる
符号が重なる
> print( Decimal(string: "++1")! )
0
> print( Decimal(string: "--1")! )
0
> print( Decimal(string: "++")! )
0
> print( Decimal(string: "--")! )
0
2個目の符号が不正でそれ以降が無視されるから、符号だけの場合と同じ動作になるみたい
指数表記 + 符号
> print( Decimal(string: "3.14159e+3")! )
3141.59
> print( Decimal(string: "3.14159e-3")! )
0.00314159
> print( Decimal(string: "3.14159e-")! )
3.14159
> print( Decimal(string: "3.14159e--3")! )
3.14159
今までの結果を踏まえると、まあ当然こうなるわな
スペース
> print( Decimal(string: "123 456 789")! )
123
スペースは不正な文字。
> print( Decimal(string: " 123")! )
123
ただ、先頭にスペースが入るのはちゃんと無視されるみたい
コンマ
print( Decimal(string: "123,456,789")! )
123
これもダメ。文書とかで表記されてるのをDecimalに入れるには整形しないといけないね。
アルファベット
> print( Decimal(string: "a") )
nil
まあ当たり前だね
> print( Decimal(string: "e")! )
0
指数表記のeだと思われてる?
演算
> print( Decimal(string: "1+2")! )
1
> print( Decimal(string: "1-2")! )
1
> print( Decimal(string: "1*2")! )
1
> print( Decimal(string: "1/2")! )
1
> print( Decimal(string: "2^2")! )
2
> print( Decimal(string: "5%3")! )
5
まあやってくれないだろうね。
それぞれをDecimal型のインスタンスにしてから普通にプログラム内で計算しろって話ですもんね。
お金
> print( Decimal(string: "¥120") )
nil
> print( Decimal(string: "$1") )
nil
もちろんダメだ。
結論は先に書いてある
のでここに書くことはない。
コメント