5

私は、Java と SQL Server の精度の悪夢に悩まされてきましたが、もうわからないほどです。個人的には、この問題とその根底にある理由は理解していますが、地球の反対側にいるクライアントにそれを説明することは (少なくとも私にとっては) 不可能なことです。

状況はこうです。SQL Server には、Qty INT と Price FLOAT の 2 つの列があります。これらの値は 1250 と 10.8601 であるため、合計値を取得するには、その数量 * 価格と結果は 13575.124999999998 になります (Java と SQL Server の両方で)。そのとおりです。問題はこれです - クライアントはそれを見たくないのです。彼らはその番号を 13575.125 としてしか見ません。それだけです。ある場所では 2 桁の精度で表示され、別の場所では 4 桁の精度で表示されます。10 進数 4 桁で表示する場合、数字は正しい - 13575.125 ですが、2 桁で表示する場合、間違っていると信じています - 13575.12 - 代わりに 13575.13 にする必要があります!

ヘルプ。

4

11 に答える 11

15

あなたの問題はあなたがフロートを使用しているということです。Java側では、floatやdoubleではなくBigDecimalを使用する必要があり、SQL側では、Decimal(19,4)(または、精度レベルにジャンプするのに役立つ場合はDecimal(19,3))を使用する必要があります。SQLのMoneyタイプの計算により、丸めではなく切り捨てが発生するため、Moneyタイプは使用しないでください。データがfloatタイプ(変更できないと言う)として保存されているという事実はこれに影響しません。計算を行う前に、最初の機会にデータを変換する必要があります。

与えた特定の例では、最初に4桁の精度の数値を取得し、場合によってはBigDecimalまたはDecimal(19,4)に入れてから、さらに2桁の精度に丸める必要があります。次に(切り上げている場合)、希望する結果が得られます。

于 2009-11-17T16:48:44.743 に答える
5

BigDecimalを使用します。Float は、お金を表すのに適した型ではありません。丸めを適切に処理します。Float は常に丸め誤差を生成します。

于 2009-11-17T16:42:22.350 に答える
3

金額を保存する場合、浮動小数点値は使用できません。あなたの説明から、私はおそらく、データベースのストレージ形式として、金額に10^5を掛けた値を持つ長整数の金額を処理します。

精度を失わない量で計算を処理できる必要があるため、ここでも浮動小数点は使用できません。元帳で借方と貸方の合計が1セントずれている場合、元帳は金融関係者の目には失敗するため、ソフトウェアが問題のあるドメインで動作していることを確認してください。既存のクラスを金額に使用できない場合はamount * 10^5、入力と出力の目的でのみ必要な精度で動作し、それに応じてフォーマットする独自のクラスを作成する必要があります。

于 2009-11-17T16:48:26.313 に答える
1

FLOAT データ型は base10 ではなく base2 であるため、分数を正確に表すことができません。(便利なリンクを参照してください:) http://gregs-blog.com/2007/12/10/dot-net-decimal-type-vs-float-type/ )。

財務計算など、分数を正確に表す必要がある場合は、DECIMAL データ型を使用する必要があります。

于 2010-11-11T04:17:00.137 に答える
1

価格に float データ型を使用しないでください。「Money」または「SmallMoney」を使用する必要があります。

[MS SQL DataTypes][1] のリファレンスは次のとおりです。

[1]: http://webcoder.info/reference/MSSQLDataTypes.html

訂正: Decimal(19,4) を使用

ありがとうイーシャイ。

于 2009-11-17T16:43:00.023 に答える
1

私は問題を見ていると思います。

10.8601 を完全に表現することはできないため、13575.125 への丸めは問題なく機能しますが、0.005 を追加してもまったく到達しないため、.13 に丸めるのは困難です。さらに悪いことに、0.005 にも正確な表現がないため、最終的には 0.13 をわずかに下回ることになります。

次に、丸めを 2 回 (1 回を 3 桁に、次に 1 回を 2 桁に) するか、最初からより適切な計算を行うかを選択します。長い形式または高精度の形式を使用して、1000 倍にスケーリングして *.125 から *125 にします。正確な整数を使用して丸めを行います。

ところで、無限に繰り返される「浮動小数点は不正確」というバリエーションの 1 つ、または常にエラーが発生するというのは完全に正しいとは言えません。問題は、この形式では、2 の負のべき乗を合計して作成できる分数しか表現できないことです。したがって、シーケンス 0.01 から 0.99 のうち、正確な表現を持つのは .25、.50、および .75 のみです。したがって、FP は、皮肉なことに、整数値のみが使用されるようにスケーリングすることによって最適に使用されます。そうすれば、整数データ型演算と同じくらい正確になります。もちろん、最初から固定小数点整数を使用したこともあるでしょう。

注意してください。たとえば、0.37 から 37 へのスケーリングは、丸められない限り正確ではありません。浮動小数点は金銭的な値に使用できますが、それは価値がある以上の作業であり、通常、必要な専門知識は利用できません。

于 2009-11-17T16:54:52.953 に答える
0

基盤となるデータベースを修正できない場合は、次のようにJavaを修正できます。

import java.text.DecimalFormat;

public class Temp {

    public static void main(String[] args) {
        double d = 13575.124999999;
        DecimalFormat df2 = new DecimalFormat("#.##");
        System.out.println( " 2dp: "+ Double.valueOf(df2.format(d)) );

        DecimalFormat df4 = new DecimalFormat("#.####");
        System.out.println( " 4dp: "+Double.valueOf(df4.format(d)) );
    }
}
于 2009-11-17T16:49:04.167 に答える
0

そもそも価格を として保存するべきではありませんが、それを、たとえば、 または にfloat変換することを検討できます(それを含む式の結果はスケールが動的に調整されないため、いくつかの問題があることに注意してください)。 SQL Server から出る途中のビューで:decimal(38, 4)moneymoney

SELECT Qty * CONVERT(decimal(38, 4), Price)
于 2009-11-17T16:53:06.607 に答える
0

したがって、データベース構造を変更できない場合 (他の多くの人が既に説明したように、固定/正確でなければならないものを表すために非固定精度を使用していることを考えると、これはおそらく最良のオプションです)、うまくいけば、どこかでコードを変更できます。Java 側では、@andy_boot が答えたようなものがうまくいくと思います。SQL 側では、基本的に、非正確な値を必要な最高の精度にキャストし、そこから引き続きキャストする必要があります。基本的に SQL コードでは次のようになります。

declare @f  float,
        @n  numeric(20,4),
        @m  money;

select  @f = 13575.124999999998,
        @n = 13575.124999999998,
        @m = 13575.124999999998

select  @f, @n, @m
select  cast(@f as numeric(20,4)), cast(cast(@f as numeric(20,4)) as numeric(20,2))
select  cast(@f as money), cast(cast(@f as money) as numeric(20,2))
于 2009-11-17T16:53:22.427 に答える
0

DecimalFormatを実行してから、それを使用して丸めることもできます。

DecimalFormat df = new DecimalFormat("0.00"); //or "0.0000" for 4 digits.
df.setRoundingMode(RoundingMode.HALF_UP);
String displayAmt = df.format((new Float(<your value here>)).doubleValue());

また、通貨を格納する DB フィールド タイプとして Float を使用すべきではないという意見にも同意します。

于 2009-11-17T17:07:07.343 に答える
0

データベースを固定小数点データ型に変更できない場合は、truncate((x+.0055)*10000)/10000 を使用して丸めを試みることができます。次に、1.124999 は 1.13 に「丸められ」、一貫した結果が得られます。数学的にはこれは信頼できませんが、あなたの場合はうまくいくと思います。

于 2009-11-17T17:10:08.663 に答える