はじめに
OutSystemsで計算処理を作っていたとき、計算結果が想定していた値と異なっていたのでRound関数の仕様について調べました。
結論としてはOutSystems特有の動きというより、OutsystemsのRound関数では最近接偶数丸めが採用されていましたよ、というお話なのですが、
備忘録として調べたことと対処方法を残しておきます。
問題の発端:四捨五入の結果が想定と異なる
ある日。四捨五入するつもりで以下のようなロジックを書きました:
Round(データの和 / データの数)
たとえば、データの和が5で、データ数が2件の場合、下記のような計算結果になりました:
5 / 2 = 2.5 → Round(2.5) = 2
3になると思ってました
OutSystemsの丸め方の仕様
OutSystemsの Round()
関数は、クライアント側とサーバー側のロジックのExpressionでは最近接偶数への丸めが使われています。
これは「0.5のような中間値を、最も近い偶数に丸める」という方法です。銀行丸めとか偶数丸めとか言われてるようです。
参考:計算
例:
式 | 結果 |
---|---|
Round(2.5) | 2 |
Round(3.5) | 4 |
Round(0.5) | 0 |
つまり、数値によって切り下げになることも、切り上げになることもあるんです。
なぜこの仕様?
「最近接偶数への丸め」は、統計的な偏りを減らすための方法です。
- 金融・会計分野でよく使われる
- 大量データの処理で平均値が安定しやすい
普通の四捨五入では、0.5がすべて上に丸められるため、平均が高くなりがちですが、
最近接偶数丸めでは、0.5が偶数に均等に丸められるため、丸め誤差が分散されて平均値が安定するのだそうです。
でも、「普通の四捨五入がしたい!」という場面もありますよね。
解決方法:普通の四捨五入(round half away from zero)がしたいときは?
方法①:条件式でどうにかする
具体的なコードについてはこちらのForumが参考になりそうです。
math-functionality-of-outsystems-to-round
注意点として、上記の方法だと負の値の場合は、-2.5が-2になります。
正負の値で一貫して0から遠ざかる方向に丸めたい(round half away from zero)なら、
絶対値を取って条件式をさらに追加したりする必要があるでしょう。
方法②:Forgeを使う
複雑な計算をするのであれば、MathUtil
みたいに検索して信頼できそうなForgeを使ったほうがいいでしょう。
番外編:AggregateでRound関数を使用した場合の挙動は?
参考:計算
こちらの公式記事には以下のように説明がありました。
Round関数を使用する場所によって結果が異なるようです。
- クライアント側とサーバー側のロジックのExpression では、最近接偶数への丸めの方法が適用されます(最も近い>整数に四捨五入。0.5は最も近い偶数の整数に丸める)。
- SQL ServerまたはOracleデータベース にクエリを実行するAggregateでは、四捨五入が適用されます(最も近>い整数に四捨五入。0.5は0から離れた数に丸める)。
- iDB2データベース にクエリを実行するAggregateでは、四捨五入が適用されます(最も近い整数に四捨五入。>0.5は切り上げる)。
つまりクライアント/サーバーサイドのロジック(Expressions)の場合は
最近接偶数丸め(round half to even)なのでこうなり、
Round(-2.5) = -2 // 最も近い偶数に丸められる
SQL Server / Oracle の Aggregateの場合は
0から遠ざかる方向に丸める(round half away from 0)のでこう、
Round(-2.5) → -3 // 0 から遠ざかる方向(-3)に丸められる
iDB2 の Aggregateの場合は
常に切り上げ(round half up)なので、こうなります。
Round(-2.5) → -2 // +∞方向に丸められる
まとめ
- OutSystemsの
Round()
は環境によって丸め方が違う - Expressionでは「最近接偶数丸め」なので、0.5が0になることもある
- 普通の四捨五入をしたいときは 条件式やForgeを使う
同じように疑問を抱いた方の参考になれば嬉しいです!