2025年4月29日 星期二

[研究]ASP.NET,WebForm,e.NewValues存入 DB 的 float 欄位後可能變了(一)

[研究]ASP.NET,WebForm,e.NewValues存入 DB 的 float 欄位後可能變了(一)

2025-04-28

環境:Visual Studio 2022 + ASP.NET + WebForm + Web Application + C# + SQL Server 2019 + SQL Server Management Studio (SSMS) 19

********************************************************************************

最近碰到一個況狀,一個小數值,e.NewValues 存入DB 的 float 欄位後,幾乎都正常,但遇到一個值居然變了的情況;在 Visual Studio 2019 中 Debug,追蹤 DetailsView1_ItemUpdating() 的 e.NewValues 值到最後,都是正常的,但DB更新結果的值卻不符;研究了一下。

********************************************************************************

ChatGPT 說:

這個問題最常見的原因是:

1. SQL Server的 float 型別本身是「近似值」(approximate numeric)

float 在 SQL Server 裡 不是精確數字,它是 IEEE 754 浮點格式,會有小數誤差。例如:

0.58 在 float 中,實際 binary 儲存時可能是類似 0.5799999xxx

然後如果你查詢時 SELECT ROUND(Rate1, 2),看到的是四捨五入後的值

但是如果系統(或者某些 ORM、自訂轉換)有先把 float 小數轉成文字或其他處理,就可能出現 0.57

簡單說:float 不適合存「需要準確小數」的資料,像利率、金額,都應該用 decimal(p,s)。

2. 你在 ASP.NET、ADO.NET 的轉型過程有細節誤差

如果你用的程式碼是這樣類型的:

float value = Convert.ToSingle(e.NewValues["Rate1"]);   

command.Parameters.AddWithValue("@Rate1", e.NewValues["Rate1"]);   

因為 e.NewValues 裡面的值其實是 object,很可能已經經過一輪型別轉換(例如從 TextBox 字串變 float),過程中又有誤差。

3. SQL Server 資料型態跟 .NET 送入時的小數精度不一致

float 預設在 SQL Server 會是 float(53),但如果你明確建表時用了 float(24) 這種低精度 float,問題會更嚴重。

float(24) 大概只有 7 位有效數字

float(53) 有 15~16 位有效數字

解決建議 
把 DB 欄位改成 **decimal(10,4)**(或其他適合的 p,s),保證 0.58 進去就是 0.5800

如果不能改 DB 結構,至少在 .NET 端明確用 decimal 處理,不要用 float

例如在後端這樣轉型:

decimal value = Convert.ToDecimal(e.NewValues["Rate1"]);
command.Parameters.Add("@Rate1", SqlDbType.Decimal).Value = value;   

並且資料表欄位是 decimal(10,4)。

********************************************************************************

未何 DB 中欄位定義是 float,但是 ASP.NET WebForm 產出的 <asp:Parameter Name="Rate1" Type="Double" /> 中是 double  ?

ChatGPT 說:

你的問題非常專業,來,我直接重點回答:

1. SQL Server 的 float 對應到 .NET 是 double

在 SQL Server 裡,float 是一種「近似數字」,但它沒有直接對應到 C# 裡的 float (C# 的 float 是 32-bit 喔)。

實際上:

SQL Server 的 float(53) (預設就是 53 位元的 float)→ 對應到 .NET C# 的 double(64-bit 浮點數)

SQL Server 的 real (低精度浮點)→ 才對應到 .NET C# 的 float(32-bit)

也就是說,SQL Server 的 float 預設就是高精度版本,所以 ASP.NET WebForm 在 Parameter 自動產生時,會設定成:

<asp:Parameter Name="Rate1" Type="Double" />

而不是 Type="Single"(Single 才是 .NET 的 float)

這是正確且預期的行為!

2. 微軟官方對應表(簡單版)

SQL Server 資料型態.NET 型態asp:Parameter Type
floatSystem.DoubleDouble
realSystem.SingleSingle
decimal / numericSystem.DecimalDecimal

3. 但是!精度問題還是存在

即使 .NET 這邊是 double,但因為 SQL Server 的 float 是近似值(approximate),小數點附近會有細微誤差,這就是為什麼你會遇到像 0.58 變成 0.57 這種情形。

********************************************************************************

SQL Server 那些資料型別 是「近似值」(approximate numeric)

ChatGPT 說:只有兩種:

資料型別說明
float浮點數,近似值,精度高(通常是 53 位元,對應 .NET 的 double)
real浮點數,近似值,但精度比 float 低(對應 .NET 的 float)

這兩個型別儲存的是「浮點表示」的數字,會有細微誤差,不是精確儲存小數位。

********************************************************************************

.NET Framework 裡,「近似值」(approximate numeric)的資料型別哪幾種

ChatGPT 說:只有兩種:

.NET 型別大小精度說明
float (System.Single)32 位元(4 bytes)大約 7 位十進位有效數字單精度浮點數(single-precision floating point)
double (System.Double)64 位元(8 bytes)大約 15~16 位十進位有效數字雙精度浮點數(double-precision floating point)

這兩種型別都是基於 IEEE 754 標準儲存方式,本質上是近似值,不是完全精確。

********************************************************************************

 .NET Framework 數值型別分類圖

數值型別
├── 精確值 (Exact Numeric)
│   ├── 整數型 (Integer Types)
│   │   ├── byte    (0 ~ 255)
│   │   ├── sbyte   (-128 ~ 127)
│   │   ├── short   (-32,768 ~ 32,767)
│   │   ├── ushort  (0 ~ 65,535)
│   │   ├── int     (-2^31 ~ 2^31-1)
│   │   ├── uint    (0 ~ 2^32-1)
│   │   ├── long    (-2^63 ~ 2^63-1)
│   │   └── ulong   (0 ~ 2^64-1)
│   └── 小數型 (Decimal Type)
│       └── decimal (28~29 位十進位有效數字,適合金額、財務運算)
│
└── 近似值 (Approximate Numeric)
    ├── 浮點型 (Floating Point Types)
    │   ├── float  (System.Single, 32-bit, 約7位有效數字)
    │   └── double (System.Double, 64-bit, 約15~16位有效數字)

********************************************************************************

如果需要儲存精確小數,SQL Server 欄位建議使用哪種資料型別,對應到 .NET Framework 是哪種型別 ?

ChatGPT 說:

SQL Server 儲存「精確小數」的最佳建議:

SQL Server 型別對應 .NET Framework 型別特性
decimal(p, s) 或 numeric(p, s)System.Decimal精確小數,絕對不失真,適合金額、比例、利率等數據

✅ decimal 跟 numeric 在 SQL Server 是完全等價的,只是名字不同,通常大家習慣用 decimal

SQL Server 型別 vs .NET 型別 完整對應表:

SQL Server 型別.NET 型別說明
decimal(p,s)System.Decimal高精度小數
numeric(p,s)System.Decimal高精度小數(跟 decimal 相同)

(其他型別像 float、real 對應到 double、float,但那是近似,不建議用。)

********************************************************************************

(完)

相關

沒有留言:

張貼留言