misc.log

日常茶飯事とお仕事と

Oracle DBのNumber型をSingleで受けたら誤差った

絶対忘れてたぶん次も調べるだろうからメモっておきます。やったのは私ではないのですが、相談を受けてちょいと調べました。

環境

起きたこと

  1. Oracle DatabaseのNumber型(小数含む)をDatasetで受けた
  2. いくつかの数値(たとえば8.5のような値)が、受けたDataset内で誤差を含んでしまった

どれくらいの誤差かというと、0.85が0.8500003とかになるくらいの誤差(正確な記録を取っていないので雰囲気だけです。桁数とか違うかもしれません)。要するに、少しだけ増えてしまったというわけです。これは困ります。

対処

Datasetの該当カラムに対応するデータ型をDecimalにする。

理由

自動で作成したのか手作業だったのかは知りませんが、データの受け取りに使っていたDatasetの該当カラムがFloat型(System.Single)で定義されていました。Singleは浮動小数点方式で数値を記録するので、誤差が出ることがあります。細かい説明は詳しい人に譲ります。私は数字の誤差に関する知識が貧弱で、実のところ正確には理解し切っていないので漠然と「誤差が出る」くらいの認識しかありません。

ま、それはさておき、誤差が出るわけです。で、マイクロソフトさんはどうしろと言っているかというと、Oracle DBのNumber型はDecimal型で受けろと言っています。

Oracleデータ型のマッピングADO.NET
http://msdn.microsoft.com/ja-jp/library/vstudio/cc716726(v=vs.90).aspx

上記リンクを参考にしてください。Decimal型は28桁までの数値を、いわば筆算でノートに数字を書いて計算するように、馬鹿正直に記録して保持するデータ型です。表現可能な桁数の範囲内であれば誤差は出ないようです(正確なところは知りませんゴメンナサイ)。というわけで、Number型を扱う場合はDecimal型で受けるのが無難です。

ちなみに、小数を含まない整数のNumber型であれば過去に実験したことがあります。

Oracle DB、Number型をExecuteScalarで取ったら…
http://www.backyrd.net/entry/20120316/p3

2012年の名古屋案件で、共通ライブラリがOracle DBからデータを受けるのに何を使うかという検討で調べてみた結果です。ODP.NETを使うと、自動的に「入ってる数値が収まる.NETの型」を用意してくれるのですが、Number型の方が上限サイズは大きいので、29桁以上であればオーバーフローします。これも、Decimalで受けていればとりあえず28桁まではどんな数値でもはいってたのですね。

もしかしたら

System.Double型であれば倍精度なので、もしかしたら上記の誤差は出ていなかったかも知れません。ですが、絶対に誤差が出ないと言うわけではないので別の数字で誤差が発生していたかもしれません。

参考にしたサイト

Microsoftの公式情報(Oracle DBと対応するデータ型)/Oracleデータ型のマッピングADO.NET
http://msdn.microsoft.com/ja-jp/library/vstudio/cc716726(v=vs.90).aspx

OTN掲示板での質問(似た感じの問題に遭遇している)/スレッド
ODP.NETを利用してデータを取得した際に、指定した小数桁以下に0が付与される:http://www.oracle.co.jp/forum/thread.jspa?threadID=35007494

@ITのInsider.NET掲示板。まさに同じ問題起きてました。/数値が近似値?に変換されてしまいます(Oracle接続)
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=34643&forum=7

Oracle 11g .NET開発者ガイド/OracleDataAdapter Safeタイプ・マッピング
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/win.111/E05791-01/featSafeType.htm

Oracle+.NETプログラミング・バイブル―ODP.NETによる強力開発環境

Oracle+.NETプログラミング・バイブル―ODP.NETによる強力開発環境