为什么这个十进制在ToString()上显示8位小数?

Gig*_*igi 4 c# sql-server entity-framework decimal

我有一个类型decimal的变量,其值为1.0.我将它保存到SQL Server 2012表的列中,其类型为decimal(10, 8).

检索该值后,我看到它是1,但是当我调用时ToString(),返回的值是"1.00000000"(见下文).

我意识到8位小数对应于数据库中的数据类型.但是,Entity Framework生成的属性中没有任何属性或任何内容可以为其提供此类行为,因此我不知道这是如何发生的.

以下是我在立即窗口中执行的一些测试:

myDecimal
1
myDecimal.ToString()
"1.00000000"
myDecimal == 1
true
myDecimal == 1.0m
true
Run Code Online (Sandbox Code Playgroud)

正如你在前两次测试中看到的那样,它也不是浮点错误的情况(不是因为十进制是定点而我没想到它,但我不得不尝试它,因为我用完了想法).

知道小数ToString()是如何产生一个8位小数的字符串?

编辑:为了比较,请查看下面的测试.

1m.ToString()
"1"
Run Code Online (Sandbox Code Playgroud)

Hug*_*une 8

原因是十进制类型未规范化.对于相同的数字有多个表示,并且这些表示将表示为不同的字符串.

这不是数据库类型的特殊属性,这是十进制正常工作的方式.没有特殊的DataAnotation或任何附加到变量的内容.

(1m).ToString() == "1"
(1.00000000m).ToString() == "1.00000000"
((1m)==(1.00000000m)) == true
Run Code Online (Sandbox Code Playgroud)

对于给定的double,只有一个有效表示,即尾数*2 指数的一个组合

对于十进制,有多个有效的尾数*10 指数表示.每个代表相同的数字,但是通过多个可能的表示可用的附加信息用于在将小数转换为字符串时选择默认的尾随数字数.确切的细节并没有很好地记录,我没有找到关于当添加或乘以小数时指数究竟发生了什么的信息.但它对ToString()的影响很容易验证.

缺点是Equals()和GetHashCode()操作比规范化数字格式更复杂,并且在实现中存在细微的错误:C#为什么等小数会产生不相等的散列值?

Jon Skeet的这篇文章详细介绍了一下:

十进制存储在128位中,即使只有102也是必需的.将十进制视为表示尾数的三个32位整数,然后是表示符号和指数的一个整数是很方便的.最后一个整数的最高位是符号位(以正常方式,位为负数设置(1))和位16-23(高16位字的低位)包含指数.其他位必须全部清零(0).此表示形式是decimal.GetBits(decimal)给出的表示形式,返回4个整数的数组.[...]

十进制类型不会对其自身进行标准化 - 它会记住它具有多少个十进制数字(通过尽可能保持指数),并且在格式化时,零可以计为有效十进制数字.

您可以通过比较decimal.GetBits()返回的值来验证您的两位小数是否相同,即:

decimal.GetBits(1m) == {int[4]}
    [0]: 1
    [1]: 0
    [2]: 0
    [3]: 0

decimal.GetBits(1.00000000m) == {int[4]}
    [0]: 100000000
    [1]: 0
    [2]: 0
    [3]: 524288
Run Code Online (Sandbox Code Playgroud)

依赖此行为来格式化小数可能很诱人,但我建议在转换为字符串时始终明确选择精度,以避免混淆和无法预料的意外,例如,如果数字预先乘以某个因子.


J. *_*een 5

当Entity Framework从查询的返回结果中检索值时,它使用您的EDMX定义或数据注释来了解要在十进制上设置的精度.

由于您使用了十进制(10,8),因此实体框架会将您的小数设置为值1.00000000.ToString十进制的实现将尊重该精度并输出所有零,因为它们被认为是重要的.

使用的类型仍然是decimal.通过给出精确值来指定小数的精度:1.000m比精确值更精确1.0m.这是多么的十进制工作,是(简述)提到这里的底部.

ToString在你说出来之前,你不知道你不认为零是重要的.零仍然是一个价值.