web-dev-qa-db-ja.com

FloatをDecimalに変換する(SQL Server)

SQL ServerでFloatをDecimal(28,10)に変換する必要があります。私の問題は、floatの性質と変換の方法により、floatをキャストするだけで、ユーザーには間違った数値のように見える可能性があることです。

例えば:

Float:               280712929.22 
Cast as Decimal:     280712929.2200000300
What I think I want: 280712929.2200000000

Floatの動作(おおよそのデータ型など)については少し理解していますが、最後に300が追加される理由を理解するには確かに十分ではありません。それは変換の副作用としての単なるゴミですか、それともフロートが実際に保存しているものをより正確に表現したものですか?私には、それは薄い空気から正確に引き出されたように見えます。

最終的には、正確であるだけでなく、「正しく」見える必要があります。末尾のゼロを追加したように見えるので、その一番下の数値を取得する必要があると思います。これは可能ですか?これは良い考えですか、それとも悪い考えですか。その理由は何ですか。他の提案は大歓迎です。

他のいくつかの例:

Float:           364322379.5731
Cast as Decimal: 364322379.5730999700
What I want:     364322379.5731000000

Float:           10482308902
Cast as Decimal: 10482308901.9999640000
What I want:     10482308902.0000000000

補足:これらの値を入力する新しいデータベーステーブルは、ユーザーが読み取ることができます。現在、実際には小数点以下2桁しか必要ありませんが、将来変更される可能性があるため、Decimal(28,10)を使用することにしました。長期的な目標は、データを取得しているfloat列も10進数に変換することです。

編集:時々、私が持っている浮動小数点数は、私がこれまでに必要とするよりも多くの小数点以下の桁数を持っています、例えば:-0.628475064730907。この状況では、-0.6284750647へのキャストで問題ありません。基本的に、小数点以下10桁になるまで、フロートの最後にゼロを追加する結果が必要です。

8
C Walker

丸めるために1回キャストし、小数点以下の桁数を戻すために1回キャストする必要があります(確かに、STRROUNDなどを使用する他の方法もあります):

DECLARE @c TABLE(x FLOAT);

INSERT @c SELECT 280712929.22;
INSERT @c SELECT 364322379.5731;
INSERT @c SELECT 10482308902;

SELECT x, 
    d = CONVERT(DECIMAL(28,10), x), 
    rd = CONVERT(DECIMAL(28,10), CONVERT(DECIMAL(28,4), x))
FROM @c;

結果:

x               d                       rd
--------------  ----------------------  ----------------------
280712929.22    280712929.2200000300    280712929.2200000000
364322379.5731  364322379.5730999700    364322379.5731000000
10482308902     10482308902.0000000000  10482308902.0000000000

正確で見栄えを良くしたい場合は、FLOATの使用をやめてください。これはおおよそのデータ型であり、厳密な数学のバックグラウンド以外のほとんどの人にとっては論理に反します。必要以上のスケールでDECIMALを使用し、現在必要な小数点以下の桁数にフォーマットします(クエリで、ビューを作成するか、計算列を作成します)。現在必要以上の情報を保存している場合は、いつでも後で公開できます。また、ユーザーにテーブルへの直接アクセスを許可しないように選択することもできます。

8
Aaron Bertrand

これがネクロポスティングに該当するかどうかはわかりませんが、最近同様の問題が発生したので、投稿するかもしれないと思いました。

これは罪として醜いかもしれませんが、うまくいくように見えました(上記のアーロンの応答から変更されました)。

DECLARE @myTable TABLE(x FLOAT);

INSERT INTO @myTable VALUES
   (280712929.22),
   (364322379.5731),
   (10482308902),   
   (-0.628475064730907);

SELECT x, 
       d = CONVERT(DECIMAL(28,10), x),                 
       NewDec = CONVERT(DECIMAL(28,10),           
                        CONVERT(DECIMAL(16,15), 
                                LEFT(CONVERT(VARCHAR(50), x, 2),17)) 
                        * POWER(CONVERT(DECIMAL(38,19),10),  
                                RIGHT(CONVERT(varchar(50), x,2),4)))                       
FROM @myTable; 

結果:

x                   d                       NewDec
------------------  ----------------------  ----------------------
280712929.22        280712929.2200000300    280712929.2200000000
364322379.5731      364322379.5731000300    364322379.5731000000
10482308902         10482308902.0000000000  10482308902.0000000000
-0.628475064730907  -0.6284750647           -0.6284750647
2
Dan

ビッグデータテーブルをfloatからdecimal(28,15)に変換する必要があるときに、この問題が発生しました。 @Danの回答のコメントで指定されているように、彼の回答は一部の値で期待どおりに機能していませんでした。

これがアップデートに使用した私の最終バージョンです

DECLARE @myTable TABLE(x FLOAT);

INSERT INTO @myTable VALUES
(280712929.22),
(364322379.5731),
(10482308902),   
(-0.628475064730907),   
(-0.62847506473090752665448522),
(8.828),
(8.9),
(8.999),
(8),
(9),
(0.000222060864421707),
(5.43472210425371E-323),
(1.73328282953587E+81);

SELECT x, 
   d = CONVERT(DECIMAL(28,15), ROUND( CONVERT(DECIMAL(28,15),           
                CONVERT(DECIMAL(16,15), 
                        LEFT(CONVERT(VARCHAR(50), x, 2),17)) 
                * POWER(CONVERT(DECIMAL(38,19),10),  
                         CASE   
  WHEN RIGHT(CONVERT(varchar(50), x,2),4) > 12 THEN 12
  ELSE RIGHT(CONVERT(varchar(50), x,2),4)
  END)), 14 )),

 SimpleVarchar = TRY_CONVERT(VARCHAR(50), x),
 AnsiVarchar = TRY_CONVERT(VARCHAR(50), x, 2)
 FROM @myTable; 

小数14を四捨五入したため、精度のごく一部が失われました。しかし、私の場合は許容範囲です。

1
Jerome2606