C# C语言中的双精度与十进制四舍五入#

C# C语言中的双精度与十进制四舍五入#,c#,double,decimal,rounding,C#,Double,Decimal,Rounding,为什么: double dividend = 1.0; double divisor = 3.0; Console.WriteLine(dividend / divisor * divisor); 产出1.0 但是: 输出0.99999999999999999999 ? 我知道1/3不能精确计算,所以必须进行舍入。 但为什么Double会将答案四舍五入到1.0,而Decimal不会呢 另外,为什么double计算1.0/3.0为0.33331? 如果使用四舍五入,那么最后的3不会四舍五入到0,

为什么:

double dividend = 1.0;
double divisor = 3.0;
Console.WriteLine(dividend / divisor * divisor);
产出1.0

但是:

输出0.99999999999999999999

?

我知道1/3不能精确计算,所以必须进行舍入。 但为什么Double会将答案四舍五入到1.0,而Decimal不会呢

另外,为什么double计算1.0/3.0为0.33331?
如果使用四舍五入,那么最后的3不会四舍五入到0,为什么是1?

为什么1/3作为双精度是0.3333 1

用二进制表示1/3的最接近方式如下: 0.0101010101... 这与系列1/4+(1/4)^2+(1/4)^3+(1/4)^4相同

当然,这受到可以存储在double中的位数的限制。double是64位,但其中一位是符号位,另一位11代表指数(把它想象成科学符号,但用二进制表示)。剩下的被称为尾数或有效位是52位。假设一个1开始,然后使用两个比特作为1/4的后续幂。这意味着您可以存储: 1/4 + 1/4^2 + ... + 1/4 ^ 27 也就是0.33331

为什么要将这三个回合乘以1

因此,以二进制表示并受双精度大小限制的1/3为: 0.010101010101010101010101010101010101010101010101010101 我不是说它是这样储存的。就像我说的,你存储的是从1开始的位,指数和符号使用不同的位。但是我认为考虑如何在基础2中编写它是有用的。 让我们继续使用“数学家的二进制”表示法,忽略双精度的大小限制。你不必这样做,但我觉得很方便。如果我们想取1/3的近似值,乘以3,这与将位移位到乘以2,然后再加上开始的值是一样的。这给了我们1/3*3=0.111111111111111111111111111111111111111111111

但是双人间能储存吗?不,记住,第一个1后面只能有52位尾数,这个数字有54位尾数。所以我们知道它会被四舍五入,在这种情况下,四舍五入到1

为什么十进制数为0.99999999999999999999999

对于decimal,您可以得到96位来表示整数,额外的位表示10的28次幂的指数。所以,尽管最终它都存储为二进制,但这里我们使用的是10的幂,所以考虑以10为基数的数字是有意义的。96位允许我们表示79228162514264337593543950335,但为了表示1/3,我们将使用所有3位,最多可以移动到小数点右侧的28位:0.3333333


将1/3的近似值乘以3,我们就得到了一个可以精确表示的数字。它只有28个9,全部移到小数点的右边:0.99999999999999999999999。因此,与double不同的是,在这一点上没有第二轮舍入。

这是通过十进制类型的设计实现的,该类型针对精度进行了优化,而不像double类型针对低精度但更高性能进行了优化

值类型表示从正79228162514264337593543950335到负79228162514264337593543950335的十进制数

十进制值类型适用于需要大量有效整数和小数位数且无舍入误差的金融计算。十进制类型并不排除四舍五入的需要。相反,它最大限度地减少了由于舍入而产生的误差。因此,您的代码生成的结果是0.999999999999999999999,而不是1


无限小数是有限小数的必要扩展的一个原因是表示分数。使用长除法,整数的简单除法(如1/9)变成循环小数,即0.111…,其中数字无终止重复。这个小数点可以快速证明0.999…=1。9乘以1,每一个数字就有9,所以9×0.111…等于0.999…9×1⁄9等于1,所以0.999…=1:

很遗憾,我没有时间读整篇文章。你能给我指一下相关的部分吗?或者如果你知道答案,就在这里输入吧?准确地说,这不是关于准确性,而是关于表示和舍入。十进制值使用更多字节来存储该值,以便获得更多有效数字,但它可能会导致类似OP的奇数舍入问题。请注意,对于
10m/3m
,我们可以使用所有29个可能的三(低于79228162514264337593543950335)(“刻度”或指数仍然是最大的28),然后再乘以
3m
,然后强制四舍五入(我们不能有29个9),结果变成
10.000000000000000000000000000m
。注意后面的零。这将与不带尾随零的
10m
进行比较。例如
double
,取
(1.0/49.0)*49.0
,该值不等于1。如果打印,它可能看起来像
“1”
,但如果使用
.ToString(“G17”)
.ToString(“R”)
您将看到它并不完全是
1
。如果你取一百万除以四十九再乘以四十九,用
double
你会得到一个错误(不是精确回到一百万),而用
decimal
你会得到一百万,后面有一堆零。
decimal dividend = 1;
decimal divisor = 3;
Console.WriteLine(dividend / divisor * divisor);