我有一个双精度小数,我想把它转换成一个小数,但是当我这样做的时候,我要么通过强制转换,要么使用Convert.ToDecimal(),这样我就失去了精度。
到底怎么回事?decimal和double都可以保存此数字:

double doub = double.Parse("138630.78380386264");
decimal dec = decimal.Parse("138630.78380386264");
string decs = dec.ToString("F17");
string doubse =DoubleConverter.ToExactString(doub);
string doubs = doub.ToString("F17");
decimal decC = (decimal) doub;
string doudeccs = decC.ToString("F17");
decimal decConv = Convert.ToDecimal(doub);
string doudecs = decConv.ToString("F17");另外:如何让ToString() on double输出与调试器显示的结果相同的结果?例如138630.78380386264
发布于 2011-09-17 17:54:39
138630.78380386264不能精确地表示为双精度。最接近的双精度数字(发现的here)是138630.783803862635977566242218017578125,这与您的发现一致。
您会问为什么到小数的转换不包含更高的精度。documentation for Convert.ToDecimal()提供了答案:
此方法返回的Decimal值最多包含15位有效数字。如果value参数包含的有效位数超过15位,则使用四舍五入将其舍入为最近的数字。下面的示例说明Convert.ToDecimal(Double)方法如何使用舍入到最近的值来返回具有15个有效数字的Decimal值。
双精度值,四舍五入到最接近的15位有效数字是138630.783803863,正如您上面所示。
发布于 2012-05-19 05:33:13
我认为这是一种不幸。在139,000附近,Decimal比Double具有更高的精度。但是,由于这个问题,我们有不同的Double被投影到 Decimal上。例如
double doub1 = 138630.7838038626;
double doub2 = 138630.7838038628;
Console.WriteLine(doub1 < doub2); // true, values differ as doubles
Console.WriteLine((decimal)doub1 < (decimal)doub2); // false, values projected onto same decimal实际上,在上面的和 doub1之间有六个不同的可表示的Double值,所以它们是不一样的。
这是一个有点愚蠢的工作:
static decimal PreciseConvert(double doub)
{
// Handle infinities and NaN-s first (throw exception)
// Otherwise:
return Decimal.Parse(doub.ToString("R"), NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint);
}"R"格式字符串确保包含足够多的额外数字,以使映射成为内射性的(在Decimal具有更高精度的域中)。
请注意,在某些范围内,long (Int64)的精度优于Double。因此,我检查了这里的转换是否以相同的方式进行(首先四舍五入到15位有效小数)。他们不是!所以:
double doub3 = 1.386307838038626e18;
double doub4 = 1.386307838038628e18;
Console.WriteLine(doub3 < doub4); // true, values differ as doubles
Console.WriteLine((long)doub3 < (long)doub4); // true, full precision of double used when converting to long当目标是decimal时,使用不同的“规则”似乎是不一致的。
注意,在这个值1.4e18附近,由于这个原因,(decimal)(long)doub3会比(decimal)doub3产生更准确的结果。
发布于 2020-02-12 01:18:29
这里的答案让您深入了解了这个问题的原因,但是(尽管这是在您问了很多年之后),这里有一种方法可以将double转换为decimal,而不需要舍入到15位数字。不过,它需要一些微不足道的东西。
与强制转换不匹配或为NaN、INF或-INF时抛出OverflowException不同,此方法在不可能进行转换时在success参数中提供false。由于decimal不支持负零,但double支持负零,所以我忽略了这一点,只返回零。
下面的代码并不完全是我的,但是我找不到原始的帖子来给予信任,抱歉。它可能取自Jon Skeet's code of ToExactString,并被用于二进制精确转换。
const decimal DecimalEpsilon = 0.0000000000000000000000000001M;
public static decimal TryToDecimalWithInsignificand(double dbl, out bool succeeded)
{
if (double.IsPositiveInfinity(dbl))
{
succeeded = false;
return decimal.MaxValue;
}
if (double.IsNegativeInfinity(dbl))
{
succeeded = false;
return decimal.MinValue;
}
if (double.IsNaN(dbl))
{
succeeded = false;
return 0M;
}
if (dbl > (double)decimal.MaxValue)
{
succeeded = false;
return decimal.MaxValue;
}
if (dbl < (double)decimal.MinValue)
{
succeeded = false;
return decimal.MinValue;
}
if (dbl > 0.0 && dbl <= (double)DecimalEpsilon)
{
succeeded = false;
return 0M;
}
if (dbl < 0.0 && dbl >= (double)-DecimalEpsilon)
{
succeeded = false;
return 0M;
}
// start conversion
long bits = BitConverter.DoubleToInt64Bits(dbl);
long mantissa = bits & 0xFFFFFFFFFFFFFL; // 52 bits
long exponent = ((bits >> 52) & 0x7FFL) - 1023L; // next 11 bits
bool negative = bits < 0;
decimal fraction = mantissa / (decimal)0x10000000000000L;
decimal result;
if (exponent < 0)
{
long div = 1 << (int)-exponent;
result = (fraction + 1) / div;
}
else
{
long mul = 1L << (int)exponent;
result = (fraction + 1) * mul;
}
succeeded = true;
if (negative)
{
return -1 * result;
}
return result;
}https://stackoverflow.com/questions/7453900
复制相似问题