アクセスログを見ていると、ときどき

http://10395.diarynote.jp/200606080926140000/

がトラップになって 「sql server float floor」や「sql trunc float」で飛んでくる人がいるんで逆突っ込み。

基本的に Microsoft SQL Server とか .NET Framework とかで float を使うべきではない。富豪プログラミング的な理論かもしれないが。

float に関してきちんと警告を出しているところとして、php のサイトがある。
http://jp2.php.net/manual/ja/language.types.float.php

ここに挙がっている例を PostgreSQL に適用して僕が試したのが
http://10395.diarynote.jp/200502170904060000/

に余談レベルで書いているが、検索エンジン様が float絡みのときは引っかけてくれない。

ちゅうわけで、 SQL Server 2008 で実験。

SELECT FLOOR((0.1 + 0.7) * 10) AS 普通の結果
SELECT FLOOR((CAST(0.1 AS FLOAT) + CAST(0.7 AS FLOAT)) * 10) AS FLOATで計算
SELECT (CAST(0.1 AS FLOAT) + CAST(0.7 AS FLOAT)) * 10 AS FLOORしない
SELECT FLOOR(CAST(0.8 AS FLOAT) * 10) AS 足し算をしない

上記の SQL を実行した結果。

普通の結果
---------------------------------------
8

(1 行処理されました)

FLOATで計算
----------------------
7

(1 行処理されました)

FLOORしない
----------------------
8

(1 行処理されました)

足し算をしない
----------------------
8

(1 行処理されました)


同じようなことを C# で。

using System;

namespace FloatTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Math.Floor(((decimal)0.1 + (decimal)0.7) * 10));
Console.WriteLine(Math.Floor((0.1F + 0.7F) * 10));
Console.WriteLine(((0.1F + 0.7F) * 10));
Console.WriteLine(Math.Floor((0.1 + 0.7) * 10));
Console.WriteLine(Math.Floor((0.8F) * 10));
}
}
}

結果
8
7
8
7
8


そういうわけで、通常のビジネスシーン、演算して整数での切り捨てとかが入っているような場合とかは float は利用せず、代わりに十進型(decimal)を利用しましょう。


・・・ただ、Oracle ではこの現象がでませんでした。ずるしてる?w

select trunc((cast(0.1 AS binary_float) + cast(0.7 AS binary_float)) * 10)
from dual;

TRUNC((CAST(0.1ASBINARY_FLOAT)+CAST(0.7ASBINARY_FLOAT))*10)
-----------------------------------------------------------
8.0E+000

---
2009年9月14日
サンプルに floor しない場合を追記

2011年2月24日
「Float を主キーにしてはいけません。」も参照。
http://10395.diarynote.jp/201102080112172931/

float型で見かけは同じ数値だけどWHERE句の = でひっかからない例も挙げています。
---
2013年2月7日追記
つまり、「SQL float」「SQL Server float」などの語句で検索しているのが間違いであり、知るべき情報はIEEE754 で決められている「float型」自体についてである。
http://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0
---
2013年3月30日追記
ふはははは。俺の時代が来た(違
http://www.oreilly.co.jp/books/9784873115894/#toc
第9章参照
---
2014年1月16日追記
Oracle の 「float 型」はIEEE754 の float型ではなく、
Number型の subtype となっており、
また Oracle には IEEE754 のfloat に対応する「binary_float型」があります。
えぇ、久しぶりに Oracle 触った時にひっかかりました。orz

コメント

nophoto
db_developer
2011年11月25日22:52

ORACLEのNUMBER型は仮数部の4桁あたり2バイトを使っています。
つまり1桁4ビットです。4ビットは、2の4乗なので、16通りの組合せを表現できますが、
そのうち10通りを使って1桁の精度を正確に保持しています。
というわけで、"ずる"はしていませんが、"贅沢"にビットを消費することで、
10進数の計算で上記の問題が起こらないようになっています。
一方、ORACLEでも2進数精度であるBINARY_FLOAT形だと、同じことが起こると思います。

NaruTo
2011年11月25日23:57

db_developerさん、コメント有難うございます。
NUMBER 型では SQL Server も PostgreSQL も問題起こりません。
「ずるしている?」というのは「きちんと検証はしていないが、BINARY_FLOAT に対する演算は一旦NUMBER型にCastして演算してBINARY_FLOAT型に戻しているように思える」という意味です。「Float型なのに Floatで演算していない~」ってことで「ずるしている」と書きましたが多彩なプラットフォーム(CPU)で動く想定なので妥当な実装だと思います。