小数を含む掛け算プログラム:「SwiftではじめるiOSアプリ開発」

 

このページは「SwiftではじめるiOSアプリ開発」(清水美樹著、工学社刊、2014年12月12日発売、ISBN978-4-7775-1869-2)の著者による関連情報です。

本書サポートページの目次へ

このサイトのトップへ


第1章において、「整数どうしの掛け算プログラム」を作成しました。二つのテキストフィールドfieldA, fieldBに整数を入力してボタンをクリックすると、ラベルresultLabelに二つの数を掛け算した結果が表示されます。
このプログラムはXcodeの使い方やGUIの書き方の実践例として作ったものですが、このテキストフィールドに、小数も入力して計算できるようにするにはどうすればいいでしょうか。
GUIの変更はありませんが、より実用的なプログラムにするために、ひとつの方法を提案します。

全部小数扱い

Doubleを扱う方法はない!?

最も簡単なのは、テキストフィールドに何を入力しても小数として扱うことです。「2」を入力したら「2.0」になります。

しかし問題は、Swiftには文字列を整数に変換する「toInt」という関数はあっても、小数に変換する「toDouble」という関数がないことです。

しかしSwiftはObjective-Cとの互換性がかなり高いので、「NSString」というObjective-Cの文字列型に簡単に変換できます。
NSString型であれば、doubleValueという関数が使えます。これは、変換できないような文字列に対しては「0.0」を返します。Swiftの「オプショナル型」で苦労しているのは一体なんなのだろうという気もしますね。

リスト1 文字列を小数に変換する例
var myDoubleValue = (mySwiftString as NSString).doubleValue


メソッドcalculateを変更

 

全部小数に変換し、掛け算した結果をそのまま出すのであれば、リスト1の方法さえ見出せばあとは簡単です。
本書では、ボタンをクリックしたときに呼び出すメソッドとしてcalculateを定義しました。その中身をリスト2のように書き換えます。

リスト2 メソッドcalculateを書き換える
@IBAction func calculate(sender: AnyObject) {
        
   let stringA = fieldA.text as NSString
   let stringB = fieldB.text as NSString
        
   let doubleA = stringA.doubleValue
   let doubleB = stringB.doubleValue
        
   let resultAB = doubleA*doubleB
        
   resultLabel.text = toString(resultAB)       
 }

実行結果

 

アプリを実行してみてください。小数を入力すると、掛け算結果が小数で表示されるようになりました。

図1 小数が使えるようになった

しかし、整数を入力しても小数で表示されるのは煩雑ですね。

図2 2x3が6.0と表示される

そこで、以下のようなプログラムにしましょう。

  1. どちらも整数が入力された場合は、整数として計算し表示する
  2. かたほうが小数の場合は、小数として計算し表示する

とすると、メソッドcalculateが複雑になりそうですね。そこで、「文字列と数値の変換」についていろいろなメソッドをもつクラスを別に作りましょう。
定義は「ViewController.swift」ファイルに、クラス「ViewController」の定義の外側に記述します。

文字列と数値の変換を扱うクラス

この例では、クラス名は「StringNumberHandler」にしました。以下のようなメソッドを定義します。全て、「タイプ・メソッド」です。

受け取ったのは整数か?

 

テキストフィールドに入力したのは、整数か小数かを判断します。
この判断は正確さを要求しません。どちらでもないものは、さらに「0」または「0.0」に変換するからです。小数点「.」があれば小数、なければ整数という判断です。

リスト3 小数点があるかないか?
class func isHandlingInt(origString:String)->Bool{        
    for ch in origString{
       if(toString(ch) == "."){
       return false
     }          
   }
  return true
}

同じ書き方でtoIntとtoDouble

上記のように、文字列を整数に変換するのと小数に変換するのはいろいろと方法が違います。そこで、このクラスのメソッドとして「toInt」と「toDouble」という名前にそれぞれまとめます。

SwiftのメソッドtoIntはオプショナルなので、リスト4のメソッドで「整数に変換できないときは0を戻す」ことを明記しています。リスト5では、Objective-CのメソッドdoubleValueでは自動的に0.0を返すので記述していません。

リスト4 SwiftのtoIntメソッドを用いるtoInt
class func toInt(origString:String)->Int{
        
   if let resultInt = origString.toInt(){
       return resultInt
    }
        
   return 0
 }
リスト5 Objective-CのdoubleValueメソッドを用いるtoDouble
 
class func toDouble(origString:String)->Double{        
    return  (origString as NSString).doubleValue        
}

以上のメソッドを定義したクラス「StringNumberHandler」はリスト6のような場所に書きます。

リスト6 Objective-CのdoubleValueメソッドを用いるtoDouble
 
import UIKit

class StringNumberHandler{
    
    class func isHandlingInt(origString:String)->Bool{
        
        for ch in origString{
            if(toString(ch) == "."){
                return false
            }     
        }
        return true
    }
    
    class func toInt(origString:String)->Int{
        
        if let resultInt = origString.toInt(){
            return resultInt
        }
        return 0
    }
    
    class func toDouble(origString:String)->Double{
        
        return  (origString as NSString).doubleValue
    }
}


class ViewController: UIViewController {
 .... クラスViewControllerの定義 ....

}

整数と小数の場合で別の処理をする

メソッドcalculateを書き換え

クラスViewControllerの定義中に書いたメソッドcalculateを、リスト7のように書き換えます。クラスStringNumberHandlerの各メソッドを使います。

リスト7 StringNumberHandlerのメソッドを用いるメソッドcalculate
 
@IBAction func calculate(sender: AnyObject) {
        
   var stringA = fieldA.text
   var stringB = fieldB.text
   
//どちらの値も整数なら     
   if(StringNumberHandler.isHandlingInt(stringA) && StringNumberHandler.isHandlingInt(stringB)){
           
            //StringNumberHandlerのメソッドtoIntを用いている
            var intResult = StringNumberHandler.toInt(stringA) * StringNumberHandler.toInt(stringB)
            resultLabel.text = toString(intResult)
    }
            
   else{ 
          var doubleA = StringNumberHandler.toDouble(stringA)
          var doubleB = StringNumberHandler.toDouble(stringB)
             
          var doubleResult = doubleA * doubleB
            
          resultLabel.text = toString(doubleResult)          
   }     
}

動作確認

アプリを実行してみましょう。どちらも整数を入力したときは整数、どちらかが小数のときは小数の結果が表示されます。

図3 整数同士の掛け算もできる

桁数を抑える

もとの小数値の桁数に合わせる

この状態で、小数同士を掛け算すると、小数点以下の桁数が多くなります。小数点以下2桁同士の掛け算の結果は、小数点以下4桁の値で表示されます。ちょっと長すぎますね。

図4 小数点以下の桁数が多すぎる

桁数があまり多くなりすぎないようにしましょう。データ処理の方法としてはかなりいい加減ですが、「もとの値で小数点以下の桁数が大きい方に合わせる」というのはどうでしょうか。


整数部分と小数点以下の数字を分離する

 

ある小数において、小数点以下の桁数を数えるにはどうすればよいでしょうか?

数学的な方法、プログラムのアルゴリズムはいろいろあると思いますが、ここでは単純な方法を用いましょう。
値がテキストフィールドを通じて、まず文字列で与えられることを利用します。
小数点である「.」という文字列を「デリミタ」にして、整数部分と、小数点以下に当たる「数字の文字列」を分離するのです。

これには、Swiftで文字列を処理するメソッド「componentsSeparatedByString」が使えます。
戻り値は文字列の配列です。「小数」に当たる文字列には「.」は一つしかありませんから、要素は2つです。

リスト8 文字列を「.」を境に二つに分ける
var array = toString(theValue).componentsSeparatedByString(".")

しかし、念のため二つ以上の要素が得られたときにだけ、その「0から数えて1番目の要素」の中身を取り出すことにします。
文字列であるその要素の文字数を数えれば、それが小数点以下の桁数になります。

リスト9 0から数えて1番目の要素があれば処理をする
if(array.count == 2){
    return countElements(array[1])
 }

何かの間違いでリスト9のif文に引っ掛からなかった場合は、0を戻します。


桁を丸めて表示する

ある小数を指定桁数で丸めるためには数学的関数を使わなければなりませんが、このアプリでは「丸めて表示」すればいいだけなので、リスト10のような書式の整形ができます。
リスト10は、小数点以下の桁数を3桁に丸めた文字列を得る書き方です。この書き方でちゃんと四捨五入して表示してくれます。

リスト10 小数3.141516の桁数を3桁で丸めた文字列を得る
String(format:"%.3f", 3.141516)

「なんの数値を処理するか」「何桁で丸めるか」をそれぞれ変数で表すには、リスト11のようにします。

リスト11 変数theValueとnumberOfDigitで表す
String(format:"%.\(numberOfDigit)f", theValue)

二つの数の大きさを比べる

「小数点以下の桁数の多い方」に合わせるとしました。「二つの数の大きさを比べる」のは他のプログラミング言語と同じです。
この関数はクラス「StringNumberHandler」に特有の処理ではないので、クラス「ViewController」の定義中にメソッドselectLargerIntとして定義しておくのがよいでしょう。

リスト11 変数theValueとnumberOfDigitで表す
func selectLargerInt(a:Int, comparedTo b:Int)->Int{
   return (a > b ?a : b)
 }

以上、メソッドcalculateを書き換えるための準備はリスト12のとおりです。

リスト12 最終的なメソッドcalculateのための準備
import UIKit

class StringNumberHandler{
    
  .... これまでの記述 ...    
  
  class func getNumberOfFractionalDigit(theValue:Double)->Int{
              
        var array = toString(theValue).componentsSeparatedByString(".")
        
        if(array.count == 2){
            return countElements(array[1])
        }
        
        return 0       
  }
    
  class func getStringForTrimmedDoubleNumber(theValue:Double, 
          inNumber numberOfDigit:Int)->String{
        
        return String(format:"%.\(numberOfDigit)f", theValue)
        
    }
    
}

class ViewController: UIViewController {

   ... これまでの記述 .....
       
    func selectLargerInt(a:Int, comparedTo b:Int)->Int{
        return (a > b ?a : b)  
    }
}

コードの完成

変数doubleAの小数点以下の桁数

小数の変数doubleAについて、小数点以下の桁数を数えるには、クラスStringNumberHandlerのメソッドgetNumberOfFractionalDigitを用います。

リスト13 メソッドgetNumberOfFractionalDigitを用いる
StringNumberHandler.getNumberOfFractionalDigit(doubleA)

doubleAとdoubleBの小数点以下の桁数を比べる

リスト13のように得られた桁数と、同じ方法で得られるdoubleBの小数点以下の桁数を、自分(ViewController)のメソッドselectLargeIntを用いて比べ、大きい方を採用します。クラスStringNumberHandlerのメソッド「getNumberOfFractionDigit」を用います。

リスト14 小数点以下の桁数の多い方が変数trimになる
 var trim = selectLargerInt(
     StringNumberHandler.getNumberOfFractionalDigit(doubleA),
     comparedTo: StringNumberHandler.getNumberOfFractionalDigit(doubleB)
  )

指定の数を指定の桁数で丸める

以上で、「指定の数(doubleResult)」を「指定の桁数(trim)」で丸めて表示する方針が固まりました。クラスStringNumberHandlerのメソッド「getStringForTrimmedDoubleNumber」を用いて、resultLabelに表示する内容が決定です。

リスト15 resultLabelの内容が決まった
resultLabel.text = StringNumberHandler.getStringForTrimmedDoubleNumber(doubleResult, inNumber: trim)

動作確認

以上、メソッドcalculateがリスト16のように完成します。

リスト16 メソッドcalculateの完成
@IBAction func calculate(sender: AnyObject) {
        
  ...これまでの記述....            
    else{

      ...これまでの記述....                      
      
     var trim = selectLargerInt(
         StringNumberHandler.getNumberOfFractionalDigit(doubleA),
          comparedTo: StringNumberHandler.getNumberOfFractionalDigit(doubleB)
     )
     
    //resultLabel.textの中身を書き換える               
     resultLabel.text = 
          StringNumberHandler.getStringForTrimmedDoubleNumber(doubleResult, inNumber: trim)    
     }          
 }

アプリを実行して、いろいろな小数や整数を入力してみてください。小数の桁数があまり多くならずに抑えられますね。

図5 小数点以下の桁数を抑えることができた


目次へ

このサイトのトップへ