2019-01-13

Oracleで有効数字を取得する。

基本的な考え方

ある数値の桁数を取得するには常用対数を用います。
FLOOR(LOG(10, n)) は次のとおり0以外の数値が最初に出現する桁を表します。

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, FLOOR(LOG(10, N)) FROM DATA;

         N FLOOR(LOG(10,N))
---------- ----------------
      1234                3
       234                2
        34                1
         4                0
      .123               -1
     .0234               -2
    .00345               -3

注: LOG(10, 0) は次のエラーを発生させます。: ORA-01428: 引数'0'が有効範囲外です

したがって、ROUND(n, - FLOOR(LOG(10, n))) で最初の1桁で丸めを行った値を取得することができます。

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, ROUND(n, - FLOOR(LOG(10, N))) FROM DATA;

         N ROUND(N,-FLOOR(LOG(10,N)))
---------- --------------------------
      1234                       1000
       234                        200
        34                         30
         4                          4
      .123                         .1
     .0234                        .02
    .00345                       .003

最初の d 桁で丸めるには、ROUND(n, d - 1- FLOOR(LOG(10, n)))とします。

関数

以上を関数にまとめると次のようになります。

CREATE OR REPLACE FUNCTION SIGNIFICANT_FIGURES(
    n   NUMBER,
    d   NUMBER
) RETURN NUMBER
IS
BEGIN
    IF n = 0 THEN
        RETURN 0;
    ELSE
        ROUND(n, d - 1 - FLOOR(LOG(10, n)));
    END IF;
END;

偶数丸めを行うには、ROUND 関数の代わりに ROUND_HALF_EVEN関数を使います。

注: Oracle Database 18c からROUND_TIES_TO_EVEN 関数が使えます。

2019-01-12

Oracleで偶数丸め(JIS丸め)

Oracle標準のROUND 関数は四捨五入です。
偶数丸め(JIS丸め、銀行丸め)を行うには関数を自作する必要があります。

基本的な考え方

単純な整数への丸めを行うケースを考えます。

  • 端数がちょうど0.5かどうかを判定するため、絶対値を1で割ります。
    余りが0.5のとき偶数丸めを行う必要がありますが、その他の場合は四捨五入と同じですので、ROUND関数が使えます。
  • 実は、偶数丸めを行うためには、絶対値を1でなく2で割った方が良いです。
    2で割った場合、余りが0.5または1.5のときに偶数丸めが必要となります。
    余りが0.5のときは切り捨て(TRUNCATE)を行い、 余りが1.5のときは切り上げ(四捨五入ですのでROUND関数が使えます)を行うことで偶数丸めが実現できます。

関数

以上を関数にまとめると、以下のようになります。

CREATE OR REPLACE FUNCTION ROUND_HALF_EVEN(
    n       NUMBER,
    integer NUMBER DEFAULT 0
) RETURN NUMBER
IS
BEGIN
    IF MOD(ABS(n) * POWER(10, integer), 2) = 0.5 THEN
        RETURN TRUNC(n, integer);
    ELSE
        RETURN ROUND(n, integer);
    END IF;
END;

Oracle Database 18c から ROUND_TIES_TO_EVEN 関数が実装されました。