首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#双精度到小数精度损失

C#双精度到小数精度损失
EN

Stack Overflow用户
提问于 2011-09-17 17:50:36
回答 3查看 35.1K关注 0票数 33

我有一个双精度小数,我想把它转换成一个小数,但是当我这样做的时候,我要么通过强制转换,要么使用Convert.ToDecimal(),这样我就失去了精度。

到底怎么回事?decimal和double都可以保存此数字:

代码语言:javascript
复制
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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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,正如您上面所示。

票数 25
EN

Stack Overflow用户

发布于 2012-05-19 05:33:13

我认为这是一种不幸。在139,000附近,DecimalDouble具有更高的精度。但是,由于这个问题,我们有不同的Double被投影到 Decimal上。例如

代码语言:javascript
复制
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,所以它们是不一样的。

这是一个有点愚蠢的工作:

代码语言:javascript
复制
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位有效小数)。他们不是!所以:

代码语言:javascript
复制
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产生更准确的结果。

票数 5
EN

Stack Overflow用户

发布于 2020-02-12 01:18:29

这里的答案让您深入了解了这个问题的原因,但是(尽管这是在您问了很多年之后),这里有一种方法可以将double转换为decimal,而不需要舍入到15位数字。不过,它需要一些微不足道的东西。

与强制转换不匹配或为NaNINF-INF时抛出OverflowException不同,此方法在不可能进行转换时在success参数中提供false。由于decimal不支持负零,但double支持负零,所以我忽略了这一点,只返回零。

下面的代码并不完全是我的,但是我找不到原始的帖子来给予信任,抱歉。它可能取自Jon Skeet's code of ToExactString,并被用于二进制精确转换。

代码语言:javascript
复制
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;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7453900

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档