当将“高”精度双倍转换为十进制时,由于四舍五入,我失去了Convert.ToDecimal或浇铸到(十进制)的精度。
例子:
double d = -0.99999999999999956d;
decimal result = Convert.ToDecimal(d); // Result = -1
decimal result = (Decimal)(d); // Result = -1Convert.ToDecimal(double)返回的十进制值最多包含15个有效数字。如果value参数包含超过15个有效位数,则使用四舍五入对其进行舍入。
因此,为了保持精度,我必须将双字符串转换为字符串,然后调用Convert.ToDecimal( String ):
decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d这个方法是有效的,但我想避免使用字符串变量来将一个双值转换为十进制,而不会在15位后舍入?
发布于 2014-07-05 13:58:24
一种可能的解决方案是将d分解为n个双倍的精确和,其中最后一个是小的,包含转换为十进制时想要的所有尾随的重要数字,而第一个(n-1)则精确地转换为十进制。
对于源-1.0到1.0之间的双d:
decimal t = 0M;
bool b = d < 0;
if (b) d = -d;
if (d >= 0.5) { d -= 0.5; t = 0.5M; }
if (d >= 0.25) { d -= 0.25; t += 0.25M; }
if (d >= 0.125) { d -= 0.125; t += 0.125M; }
if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; }
t += Convert.ToDecimal(d);
if (b) t = -t;在ideone.com上进行测试。
请注意,d -=操作是精确的,即使C#以比double更高的精度计算二进制浮点操作(这是允许自己执行的)。
这比从double到string的转换更便宜,并且在结果中提供了几个额外的精度数字(上述四个如果-然后的结果提供了四位精度)。
备注:如果C#不允许自己以更高的精度进行浮点计算,那么一个很好的技巧就是使用Dekker分裂将d拆分成两个值d1和d2,将每个值都精确地转换为十进制。唉,Dekker分裂只对IEEE 754乘法和加法有严格的解释。
另一种思路是利用C#的弗雷克斯版本得到d的意义和指数e,并计算(Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>。
发布于 2014-07-08 17:12:33
有两种方法,一种适用于2^63以下的值,另一种适用于大于2^53的值。
将较小的值分割成整数和小数部分。整数部分可以精确地转换到long,然后Decimal注意到直接转换到Decimal可能不精确!分数部分可精确乘以9007199254740992.0 (2^53),转换为long,然后再转换为Decimal,再除以9007199254740992.0m。将该除法的结果添加到整数部分应该会产生一个Decimal值,该值在正确的最小显着数字内,可能不会被精确舍入,但仍然会比内置转换好得多!
对于较大的值,乘以(1.0/281474976710656.0) (2^-48),取结果的整数部分,再乘以281474976710656.0,然后从原来的结果中减去它。将除法和减法的整数结果转换为Decimal (它们应该精确地转换),将前者乘以281474976710656m,再加上后者。
https://stackoverflow.com/questions/24586969
复制相似问题