DoubleやFloat、CGFloatなどを丸める(切り上げ、切り捨て、四捨五入など)ための関数として、Swiftの標準ライブラリに round と rounded が追加されて久しいのはみなさんご存知のことかと思います。
ただ、これに渡すルール(FloatingPointRoundingRuleの値)をいつも迷うため、この記事にてまとめたいと思います。
ここではSwift5.3で記載していますが、round, rounded自体は3.0から追加されています。
最初に結論から
こんな感じになります。
元の値 | 6.5 | 5.5 | 5.4 | -5.4 | -5.5 | -6.5 |
↓ | ↓ | ↓ | ↓ | ↓ | ↓ | |
.up | 7.0 | 6.0 | 6.0 | -5.0 | -5.0 | -6.0 |
.down | 6.0 | 5.0 | 5.0 | -6.0 | -6.0 | -7.0 |
.towardZero | 6.0 | 5.0 | 5.0 | -5.0 | -5.0 | -6.0 |
.awayFromZero | 7.0 | 6.0 | 6.0 | -6.0 | -6.0 | -7.0 |
.toNearestOrAwayFromZero | 7.0 | 6.0 | 5.0 | -5.0 | -6.0 | -7.0 |
.toNearestOrEven | 6.0 | 6.0 | 5.0 | -5.0 | -6.0 | -6.0 |
以下、round, rounded の詳細について記載していきます。
round, roundedの定義
round と rounded の定義は以下のようになっています。(Doubleの場合)
public func rounded() -> Double
public func rounded(_ rule: FloatingPointRoundingRule) -> Double
public mutating func round()
public mutating func round(_ rule: FloatingPointRoundingRule)
使い方としては以下のようになります。
let roundedValue = (5.5).rounded(.up)
print(roundedValue) // 6.0
var value = 5.5
value.round(.down)
print(value) // 5.0
roundedは丸めた値を返して、元の値は変更しません。
上では数値リテラルから呼び出していますが、もちろん定数や変数からでも問題ありません。
roundはmutatingとなっており、変数から呼び出して自らの値を変更します。
こちらは変数からのみ呼び出し可能となっています。
FloatingPointRoundingRule
ruleの引数に渡す FloatingPointRoundingRule の値とその意味は以下になります。
.up
切り上げを行います。
切り上げとは、端数が生じた場合、数の大きい方へ寄せて丸める処理のことを言います。
5.x であれば 6.0 に、-5.x であれば -5.0 になります。(xは0以外の任意の数字)
ceil関数もこれと同じ結果になります。
.down
切り捨てを行います。
切り捨てとは、端数が生じた場合、数の小さい方へ寄せて丸める処理のことを言います。
5.x であれば 5.0 に、-5.x であれば -6.0 になります。(xは0以外の任意の数字)
floor関数もこれと同じ結果になります。
.towardZero
0に近くなるように丸めます。
これは、端数が生じた場合、0に近い数へ寄せて丸める処理を行います。
5.x であれば 5.0 に、-5.x であれば -5.0 になります。(xは0以外の任意の数字)
.awayFromZero
0から遠くなるように丸めます。
これは、端数が生じた場合、0から遠い数へ寄せて丸める処理を行います。
5.x であれば 6.0 に、-5.x であれば -6.0 になります。(xは0以外の任意の数字)
.toNearestOrAwayFromZero
いわゆる四捨五入をします。
これは、まずtoNearestOrAwayFromZeroの名前のとおり、x.5 (xは正負関わらず任意の数字)を境として、より近い方の整数に寄せます。5.0 < n < 5.5 なら 5.0 に、5.5 < n < 6.0 なら 6.0 になります。負数も同様に、-5.5 < n < -5.0 なら -5.0 に、-6.0 < n < -5.5 なら -6.0 になります。
そして、ちょうど x.5 だった場合は、toNearestOrAwayFromZeroのとおり0から遠い方へ寄せます。5.5 なら 6.0 に、-5.5 なら -6.0 になります。
ちなみに、Appleのリファレンスを見ると以下のような記述があります。
This rounding rule is also known as “schoolbook rounding.”
Apple Developer Document – https://developer.apple.com/documentation/swift/floatingpointroundingrule/tonearestorawayfromzero
「教科書の丸め処理」ってことでしょうか。
アメリカも日本と同様、学校で習うアレという認識なんですね😊
.toNearestOrEven
銀行家の丸め (bankers rounding) と呼ばれる丸め処理を行います。
まずNearestの部分は上記 toNearestOrAwayFromZero と同じです。近い方に寄せます。
そしてちょうど x.5 の場合、Even の名が示すとおり、偶数になるように丸めます。
5.5 なら 6.0 に、6.5 なら 7.0 にはならず 6.0 になります。負数も同様、-5.5 は -6.0 に、-6.5 も -6.0 になります。
この「銀行家の丸め」については、以下のサイト様の記事がとても参考になりました 🙇♂️ 感謝
会計SEのメモ – 「銀行家の丸め」とは何か
要するに、数値を丸めながら集計を行った場合、単純な四捨五入よりも誤差が少なくなるので銀行家に好まれた、ということのようです。
おもしろい。
お試し用コード
最後に、自分で動かして確認したい方向けにサンプルコードを載せておきます。
PlaygroundやXCTestなどにコピペしてお使いください。
import Foundation
let values: [Double] = [6.51, 6.5, 6.49, 5.51, 5.5, 5.49, 5.4, -5.4, -5.49, -5.5, -5.51, -6.49, -6.5, -6.51]
let rules: [FloatingPointRoundingRule] = [.up, .down, .towardZero, .awayFromZero, .toNearestOrAwayFromZero, .toNearestOrEven]
// rounded
rules.forEach { rule in
print("rule: \(rule)")
values.forEach { value in
print("\(value) -> \(value.rounded(rule))")
}
print("")
}
// round
print("round")
values.forEach { value in
print("\(value) -> \(round(value))")
}
print("")
// ceil
print("ceil")
values.forEach { value in
print("\(value) -> \(ceil(value))")
}
print("")
// floor
print("floor")
values.forEach { value in
print("\(value) -> \(floor(value))")
}
print("")
参考リンク
Apple Developer Document – FloatingPointRoundingRule
https://developer.apple.com/documentation/swift/floatingpointroundingrule