コルネの進捗や備忘録が記されたなにか

進捗や成果物や備忘録てきななにかを雑に更新していきます。

Power Automateで数値の切捨て / 切上げ / 四捨五入を行う


スポンサードリンク

はじめに

Power AppsにはRound、RoundDown、RoundUp、および Trunc関数という切捨て、切上げ、四捨五入を行う関数があるのに、なんでPower Automateにはそれと同等の関数がないんですかね...?

docs.microsoft.com

不便じゃない?と思ったので切捨て、切上げ、四捨五入を行う式を作成してみました。

ものすごくだるかったですね。

インプット値

インプット値は、切捨て、切上げ、四捨五入をする数値と少数N桁まで表示させる。ということを表す数値です。

名前 内部名
triggerBody()['number']
小数点以下 triggerBody()['number_1']

内部名 = 数式内で扱うときの動的な値です。
数式をコピペする際に、適宜置き換えてください。

四捨五入

これが一番簡単ですね。
四捨五入というより正確にいうと丸めですが。

float(
    formatNumber(triggerBody()['number'], concat('F', triggerBody()['number_1']))
)

ただ、数値のフォーマットを行っているだけですね。

F[N]で少数N桁の数値に丸めます。

F3だと少数3桁の数値に丸められますね。

詳しくはHiroさんのブログを参照。

qiita.com

その他フォーマットはこちらを参照。

docs.microsoft.com

2022/12/17 追記

コメントにて、切上げの際に桁上がりするケースが正しく動いていないとの指摘をいただきましたので修正を行いました。

切捨て/切上げともにやり方を変更し、割とシンプルなものに修正できました。

一応以前のやり方も消さずに残してはおきます。

切捨て

切捨ては判定対象となる桁の数値(例えば、小数第2位まで残すのであれば小数第3位が判定対象)を0にしたうえで、四捨五入を行えば確実に切捨てされますね。

判定対象の桁の数値を0にするには、

 {元の数値} - \frac{1}{10^{切捨てを行う桁}} * {元の数値の判定対象の数}

で計算してあげれば0にできます。
具体的な数値で表すのであれば、

  • 元の数値: 10.092
  • 切捨てを行う桁: 3

とした場合、以下となりますね。

 10.092 - \frac{1}{10^{3}} * 2 = 10.09

ただし、残念なことにPower Automateには累乗を計算する関数は用意されていません。
ドウシテ。。。

累乗を式関数だけで表現するのは少しハードルが高いので発想を変えてみましょう。

今回欲しいのは  10^{N} であり、基数(底)は10で固定です。
つまり1, 10, 100, 1000・・・みたいな数値を作成できればいいわけですね。

これを実現するためにjoin 関数range 関数をうまいこと活用します。

range関数は第一引数で指定した数値から始まる連続した数値の配列を、第二引数で指定した数値の数だけ作成してくれる関数で、join関数は配列を指定した区切り文字で結合した文字列を返してくれる関数でしたね。

range関数を用いて1~Nまでの連続した数値の配列を作成して、join関数で結合してあげれば、N桁目が1のN桁の数値が作成できます。

式にすると以下ですね。(今回NはN桁目まで保持としているので、1余計に加算されています。)

sub(
    int(
        join(
            range(
                1, 
                add(triggerBody()['number_1'], 2)
            ), 
            ''
        )
    ),
    int(
        join(
            range(
                2, 
                add(triggerBody()['number_1'], 1)
            ), 
            ''
        )
    )
)

ただしこれですと、N + 2が10を超えると上手くいきません。
12345678910
と桁数が変わってしまうからですね。

それを対策するためにtake 関数で指定桁のみ取得するようにします。

sub(
    int(
       take(
           join(
               range(
                   1, 
                   add(triggerBody()['number_1'], 2)
               ), 
               ''
           ),
           add(triggerBody()['number_1'], 2)
        )
    ),
    int(
       take(
           join(
               range(
                   2, 
                   add(triggerBody()['number_1'], 1)
               ), 
               ''
           ),
           add(triggerBody()['number_1'], 1)
        )
    )
)

私のこの書き方の他にも、subで引き算を行った結果に対してtakeを実施するのでもOKです。
ただしその場合、takeで返されるのは文字列なので、int関数で数値に戻すことを忘れないでください。

私が例のようにやっているのは文字列 ⇆ 数値の変換回数をなるべく減らして後から見返した際などに混乱しないようにするためですね。

型を無暗に変えすぎるのはバグの元ですので。

あとは、上で説明した内容を式に起こすだけですが、1点注意が必要です。

div関数は引数に設定した型を引き継いでしまうので、 div(1, 10) みたいにしてしまうと 0 が返ってきてしまいます。
それを回避するために、 div(float(1), 10) のようにしてあげましょう。

最終的に以下のような式で切捨てが行えます。

float(
   formatNumber(
       sub(
           triggerBody()['number'],
           mul(
               div(
                   float(1),
                   sub(
                       int(
                           take(
                               join(
                                   range(
                                       1, 
                                       add(triggerBody()['number_1'], 2)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 2)
                           )
                       ),
                       int(
                           take(
                               join(
                                   range(
                                       2, 
                                       add(triggerBody()['number_1'], 1)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 1)
                           )
                       )
                   )
               ),
               int(last(string(triggerBody()['number'])))
           )
       ),
       concat('F', triggerBody()['number_1'])
   )
)

以下古いやり方(以前まで公開していたもの)

切上げ

続いて切上げですが、ロジックは切捨てとほぼほぼ同じです。
切捨ての場合は四捨五入して切り捨てられるように0に設定していましたが、今度は切り上げられるように9になるように設定してあげます。

これは
 {対象の数} + (9 - {対象の数})

のようにやってあげれば、四捨五入を行う箇所が9となるので、切上げが行われますね。

切上げを行う箇所の数値は「.」が出てくるまでの文字数 + 残したい桁数 + 1で取得できます。
これを式にすると以下のようになります。

float(
   formatNumber(
       add(
           triggerBody()['number'],
           mul(
               div(
                   float(1),
                   sub(
                       int(
                           take(
                               join(
                                   range(
                                       1, 
                                       add(triggerBody()['number_1'], 2)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 2)
                           )
                       ),
                       int(
                           take(
                               join(
                                   range(
                                       2, 
                                       add(triggerBody()['number_1'], 1)
                                   ), 
                                   ''
                               ),
                               add(triggerBody()['number_1'], 1)
                           )
                       )
                   )
               ),
               sub(
                   9,
                   int(
                       last(
                           take(
                               string(triggerBody()['number']),
                               add(
                                   indexOf(
                                       string(triggerBody()['number']), 
                                       '.'
                                   ),
                                   add(1, triggerBody()['number_1'])
                               )
                           )
                       )
                   )
               )
           )
       ),
       concat('F', triggerBody()['number_1'])
   )
)

以下古いやり方(以前まで公開していたもの)

おわりに

なんか切上げの数式ごちゃごちゃやっちゃったけど、もうちょっと簡単にかけそうな気がしますね。

2022/12/17 追記
文字列変換をしまくってやるよりは多少マシになりましたかね?
これぐらい標準関数で搭載して欲しいですねー


スポンサードリンク