C#小数类型舍入不当
我有一个夏令营的内部银行解决方案,我是夏令营的一部分。接下来的问题是,一些账户在本赛季结束时正好被减了一分钱 我使用的是SQL Server Express,有两个表。一个包含所有账户的表,其中保存了账户的当前余额。另一个表是正在运行的事务日志。这个想法是,如果我要对日志中的所有事务进行汇总,它应该等于任何给定帐户的当前帐户余额 但事实并非如此。例如,我对数据库求和,发现给定的帐户应该有C#小数类型舍入不当,c#,sql-server,floating-point,rounding-error,C#,Sql Server,Floating Point,Rounding Error,我有一个夏令营的内部银行解决方案,我是夏令营的一部分。接下来的问题是,一些账户在本赛季结束时正好被减了一分钱 我使用的是SQL Server Express,有两个表。一个包含所有账户的表,其中保存了账户的当前余额。另一个表是正在运行的事务日志。这个想法是,如果我要对日志中的所有事务进行汇总,它应该等于任何给定帐户的当前帐户余额 但事实并非如此。例如,我对数据库求和,发现给定的帐户应该有$15.10,但帐户表显示它们的余额为$15.09 以下是相关代码: //Amount is negative
$15.10
,但帐户表显示它们的余额为$15.09
以下是相关代码:
//Amount is negative, they bought something
if (amount <= 0)
{
//Check if they have enough money to buy, or they are a paid account type (faculty, staff)
if (account.Balance >= Math.Abs(amount) || account.AccountType > AccountType.Camper)
account.Balance -= Math.Abs(amount);
else
return Error.LowBalance;
}
else
{
account.Balance += Math.Abs(amount);
}
var t = new Transaction
{
AccountId = account.Id,
Amount = amount,
SubmittedBy = username,
TransactionRefId = refId,
ComputerHostname = hostname,
Location = location,
Notes = notes
};
_db.Transactions.Add(t);
_db.SaveChanges();
//金额为负数,他们买了东西
if(amount=Math.Abs(amount)| | account.AccountType>AccountType.Camper)
账户余额-=数学资产负债表(金额);
其他的
返回错误。低平衡;
}
其他的
{
账户余额+=数学资产负债表(金额);
}
var t=新交易
{
AccountId=account.Id,
金额=金额,
SubmittedBy=用户名,
TransactionRefId=refId,
计算机主机名=主机名,
位置=位置,
注释=注释
};
_db.Transactions.Add(t);
_db.SaveChanges();
我使用的是实体框架,首先是代码。账户.余额
、金额
和交易.金额
的数据类型均为系统十进制
我不明白为什么会有舍入错误,我正在使用所有适合这种情况的数据类型。我看到这一点的通常方式是,当他们有两个不同的交易,$0.75
。出于某种原因,代码显示0.05+0.05=0.09
在我的数据库中,Accounts.Balance
列的数据类型是decimal(18,2)
我知道浮点数有时可以做到这一点,但我正在寻找解决办法。我以为我已经使用了
系统.Decimal
数据类型了。您只存储了2个十进制数字,然后丢弃了后面的所有内容,在这个过程中失去了精度。存储更多的十进制数字
另一个丢失精度的例子:您只存储了2个十进制数字,并将其后面的所有内容都丢弃,从而在存储过程中丢失了精度。存储更多的十进制数字
另一个丢失精度的例子:你所说的“代码表示0.05+0.05=0.09”是什么意思?不清楚您在哪里看到了什么。您是否可以使用调试器找到导致舍入错误的确切代码行?您是否尝试过将类型更改为Money@这似乎就是正在发生的事情。如果我申请一笔0.75美元的交易,然后再申请一笔0.75美元的交易,系统显示余额等于1美元。49@AdamSchiavone如果这是正确的(老实说,我怀疑不是),那么你应该能够很容易地找到一个最小的复制机。编写一个程序,将属性设置为
1.50m
,并将其保存到数据库中。如果它存储的不是1.50
,则编辑您的问题以显示具有该行为的程序。如果它存储了1.50
,那么问题不在于你说的是什么。你所说的“代码是说0.05+0.05=0.09”是什么意思?不清楚您在哪里看到了什么。您是否可以使用调试器找到导致舍入错误的确切代码行?您是否尝试过将类型更改为Money@这似乎就是正在发生的事情。如果我申请一笔0.75美元的交易,然后再申请一笔0.75美元的交易,系统显示余额等于1美元。49@AdamSchiavone如果这是正确的(老实说,我怀疑不是),那么你应该能够很容易地找到一个最小的复制机。编写一个程序,将属性设置为1.50m
,并将其保存到数据库中。如果它存储的不是1.50
,则编辑您的问题以显示具有该行为的程序。如果它存储1.50
,那么问题不在于你说的是什么。所以我需要将decimal(18,2)
更改为类似decimal(18,4)
?或者更多,取决于你想要的可靠性,C中的decimal只是一个“多位的双精度”,但也不能被信任。我不相信这个答案适用。这篇文章是关于当你将一个10位小数的数字保存到一个只有2位小数的db字段中时的截断。它是截断的,不是圆形的。在他的情况下,数据库有足够的空间来正确存储值。@AlexandreBoreladecimal
不仅仅是更高的精度double
。decimal
类型将值表示为整数乘以10的幂(从技术上讲,始终是10的负幂),而不是2的幂。这意味着decimal
可以精确地表示1/5(2*10^-1),而double
不能。这与建议的解决方案直接相关。因此,我需要将十进制(18,2)
更改为类似十进制(18,4)
?或更多,取决于您想要的可靠性,C#中的十进制只是一个“具有更多位的双精度”,但也不能被信任。我不相信这个答案适用。这篇文章是关于当你将一个10位小数的数字保存到一个只有2位小数的db字段中时的截断。它是截断的,不是圆形的。在他的情况下,数据库有足够的空间来正确存储值。@AlexandreBoreladecimal
不仅仅是更高的精度double
。decimal
类型将值表示为整数乘以10的幂(从技术上讲,始终是10的负幂),而不是2的幂。这意味着decimal
可以精确地表示1/5(2*10^-1),而double
不能。这与提议的解决方案直接相关。