C# Convert.ToDouble(十进制)意外丢失精度

C# Convert.ToDouble(十进制)意外丢失精度,c#,C#,考虑以下代码: double d = 1478110092.9070129; decimal dc = 1478110092.9070129M; Console.WriteLine(d.ToString("R")); Console.WriteLine(dc); Console.WriteLine(Convert.ToDouble(dc).ToString("R")); Console.WriteLine(double.Parse(dc.ToString()).ToString("R"));

考虑以下代码:

double d = 1478110092.9070129;
decimal dc = 1478110092.9070129M;

Console.WriteLine(d.ToString("R"));
Console.WriteLine(dc);

Console.WriteLine(Convert.ToDouble(dc).ToString("R"));
Console.WriteLine(double.Parse(dc.ToString()).ToString("R"));
这将产生以下结果:

1478110092.9070129
1478110092.9070129
1478110092.9070127
1478110092.9070129

我的问题是Convert.ToDouble中发生了什么?很明显,这个数字可以用双精度表示?

双精度只能精确到小数点后16位,因此重复计算会很快累积舍入误差。十进制大约有28位小数精度。Double可以处理更大的数字,但除非您处理的是大型科学计算,并且了解任何精度损失对您意味着什么,否则请始终使用十进制,尤其是用于货币计算

关于这个问题有一些很好的讨论

编辑
请看下面的评论-我误读了这个问题,另一个用户(user1666620)在评论中提供了一个关于数字实际存储方式的合法答案。在双精度的情况下,这是一种不能正确表示分数的二进制表示。

我认为这是转换为双精度(十进制d)中的一个错误。C#规范说转换应该给出最接近的双精度,但这里显然没有。 看看这些位子,我们可以看到它的偏移量是一倍

double d = 1478110092.9070129;
decimal dc = 1478110092.9070129M;
double dcd = Convert.ToDouble(dc);
long d_bits = BitConverter.DoubleToInt64Bits(d);     // 4743986451068882048
long dcd_bits = BitConverter.DoubleToInt64Bits(dcd); // 4743986451068882047
另请参见此可能的副本:


要了解为什么会发生这种情况,我们需要查看Decimal的内部结构

事实上,我们看到它确实如此

public static double ToDouble(decimal value) {
    return (double)value;
}
在Decimal的源代码中,我们可以看到

及其

然后我们需要去

这就引出了
OleAut32.dll
中的一个windows函数,我们没有该函数的源代码

问题可能是
OleAut32.dll
没有执行它可能执行的最大感知转换

如果要使用max percision进行转换,需要首先使用
R
格式转到字符串,然后解析字符串


如果你好奇的话,我通过一个反编译器运行了
VarR8FromDec
,下面是它内部的工作

HRESULT __stdcall VarR8FromDec(const DECIMAL *pdecIn, DOUBLE *pdblOut)
{
  BYTE v2; // dl@1
  BYTE v3; // bl@2
  double v4; // st7@4
  HRESULT result; // eax@8

  v2 = pdecIn->scale;
  if ( v2 > 0x1Cu || (v3 = pdecIn->sign, v3 & 0x7F) )
  {
    result = -2147024809;
  }
  else
  {
    if ( (pdecIn->Mid32 & 0x80000000u) == 0 )
      v4 = ((double)pdecIn->Hi32 * 1.844674407370955e19 + (double)*(signed __int64 *)&pdecIn->Lo32) / sub_1006AC0D(v2);
    else
      v4 = ((double)*(signed __int64 *)&pdecIn->Lo32 + 1.844674407370955e19 + (double)pdecIn->Hi32
                                                                            * 1.844674407370955e19)
         / sub_1006AC0D(v2);
    if ( v3 )
      v4 = -v4;
    *(_QWORD *)pdblOut = *(_QWORD *)&v4;
    result = 0;
  }
  return result;
}

double __fastcall sub_1006AC0D(unsigned int a1)
{
  double result; // st7@2

  if ( a1 > 0x50 )
    result = sub_1006ABD4((void *)a1, 10.0);
  else
    result = dbl_1002EF08[a1];
  return result;
}

double __thiscall sub_1006ABD4(void *this, double a2)
{
  unsigned int v2; // eax@1
  double v3; // st6@3
  double i; // st5@3
  double result; // st7@9

  v2 = (unsigned int)this;
  if ( (signed int)this < 0 )
    v2 = -(signed int)this;
  v3 = 1.0;
  for ( i = a2; ; i = i * i )
  {
    if ( v2 & 1 )
      v3 = v3 * i;
    v2 >>= 1;
    if ( !v2 )
      break;
  }
  if ( (signed int)this >= 0 )
    result = v3;
  else
    result = 1.0 / v3;
  return result;
}

.text:1002EF08 ; double dbl_1002EF08[]
.text:1002EF08 dbl_1002EF08    dq 1.0                  ; DATA XREF: VarDecFromR4:loc_1002F23Ar
.text:1002EF08                                         ; VarDecFromR8:loc_1002F4BAr ...
.text:1002EF10                 dd 0
.text:1002EF14                 dd 40240000h, 0
.text:1002EF1C                 dd 40590000h, 0
.text:1002EF24                 dd 408F4000h, 0
.text:1002EF2C                 dd 40C38800h, 0
.text:1002EF34                 dd 40F86A00h, 0
.text:1002EF3C                 dd 412E8480h, 0
.text:1002EF44                 dd 416312D0h, 0
.text:1002EF4C                 dd 4197D784h, 0
.text:1002EF54                 dd 41CDCD65h, 20000000h, 4202A05Fh, 0E8000000h, 42374876h
.text:1002EF54                 dd 0A2000000h, 426D1A94h, 0E5400000h, 42A2309Ch, 1E900000h
.text:1002EF54                 dd 42D6BCC4h, 26340000h, 430C6BF5h, 37E08000h, 4341C379h
.text:1002EF54                 dd 85D8A000h, 43763457h, 674EC800h, 43ABC16Dh, 60913D00h
.text:1002EF54                 dd 43E158E4h, 78B58C40h, 4415AF1Dh, 0D6E2EF50h, 444B1AE4h
.text:1002EF54                 dd 64DD592h, 4480F0CFh, 0C7E14AF6h, 44B52D02h, 79D99DB4h
.text:1002EF54                 dd 44EA7843h, 2C280291h, 45208B2Ah, 0B7320335h, 4554ADF4h
.text:1002EF54                 dd 0E4FE8402h, 4589D971h, 2F1F1281h, 45C027E7h, 0FAE6D721h
.text:1002EF54                 dd 45F431E0h, 39A08CEAh, 46293E59h, 8808B024h, 465F8DEFh
.text:1002EF54                 dd 0B5056E17h, 4693B8B5h, 2246C99Ch, 46C8A6E3h, 0EAD87C03h
.text:1002EF54                 dd 46FED09Bh, 72C74D82h, 47334261h, 0CF7920E3h, 476812F9h
.text:1002EF54                 dd 4357691Bh, 479E17B8h, 2A16A1B1h, 47D2CED3h, 0F49C4A1Dh
.text:1002EF54                 dd 48078287h, 0F1C35CA5h, 483D6329h, 371A19E7h, 48725DFAh
.text:1002EF54                 dd 0C4E0A061h, 48A6F578h, 0F618C879h, 48DCB2D6h, 59CF7D4Ch
.text:1002EF54                 dd 4911EFC6h, 0F0435C9Eh, 49466BB7h, 0EC5433C6h, 497C06A5h
.text:1002EF54                 dd 0B3B4A05Ch, 49B18427h, 0A0A1C873h, 49E5E531h, 8CA3A8Fh
.text:1002EF54                 dd 4A1B5E7Eh, 0C57E649Ah, 4A511B0Eh, 76DDFDC0h, 4A8561D2h
.text:1002EF54                 dd 14957D30h, 4ABABA47h, 6CDD6E3Eh, 4AF0B46Ch, 8814C9CEh
.text:1002EF54                 dd 4B24E187h, 6A19FC41h, 4B5A19E9h, 0E2503DA9h, 4B905031h
.text:1002EF54                 dd 5AE44D13h, 4BC4643Eh, 0F19D6057h, 4BF97D4Dh, 6E04B86Dh
.text:1002EF54                 dd 4C2FDCA1h, 0E4C2F344h, 4C63E9E4h, 1DF3B015h, 4C98E45Eh
.text:1002EF54                 dd 0A5709C1Bh, 4CCF1D75h, 87666191h, 4D037269h, 0E93FF9F5h
.text:1002EF54                 dd 4D384F03h, 0E38FF872h, 4D6E62C4h, 0E39FB47h, 4DA2FDBBh
.text:1002EF54                 dd 0D1C87A19h, 4DD7BD29h, 463A989Fh, 4E0DAC74h, 0ABE49F64h
.text:1002EF54                 dd 4E428BC8h, 0D6DDC73Dh, 4E772EBAh, 8C95390Ch, 4EACFA69h
.text:1002EF54                 dd 0F7DD43A7h, 4EE21C81h, 75D49491h, 4F16A3A2h, 1349B9B5h
.text:1002EF54                 dd 4F4C4C8Bh, 0EC0E1411h, 4F81AFD6h
HRESULT\uu stdcall VarR8FromDec(常量十进制*pdecIn,双精度*pdblOut)
{
字节v2;//dl@1
字节v3;//bl@2
双v4;//st7@4
HRESULT结果;//eax@8
v2=pdecIn->刻度;
如果(v2>0x1Cu | |(v3=pdecIn->sign,v3&0x7F))
{
结果=-2147024809;
}
其他的
{
如果((pdecIn->Mid32&0x80000000u)==0)
v4=(双精度)pdecIn->Hi32*1.844674407370955e19+(双精度)*(带符号的int64*)和pdecIn->Lo32)/sub_1006AC0D(v2);
其他的
v4=(双精度)*(带符号的)pdecIn->Lo32+1.844674407370955e19+(双精度)pdecIn->Hi32
*1.844674407370955e19)
/sub_1006AC0D(v2);
如果(v3)
v4=-v4;
*pdblOut=*(*QWORD*)和v4;
结果=0;
}
返回结果;
}
双快速呼叫子1006AC0D(无符号整数a1)
{
双重结果;//st7@2
如果(a1>0x50)
结果=sub_1006ABD4((无效*)a1,10.0);
其他的
结果=dbl_1002EF08[a1];
返回结果;
}
双倍本呼叫子呼叫1006ABD4(无效*本,双倍a2)
{
无符号整数v2;//eax@1
双v3;//st6@3
双i;//st5@3
双重结果;//st7@9
v2=(无符号整数)此;
如果((带符号int)此值小于0)
v2=-(带符号的int)此;
v3=1.0;
对于(i=a2;;i=i*i)
{
如果(v2和1)
v3=v3*i;
v2>>=1;
如果(!v2)
打破
}
如果((签名整数)此>=0)
结果=v3;
其他的
结果=1.0/v3;
返回结果;
}
.文本:1002EF08;双dbl_1002EF08[]
.文本:1002EF08 dbl_1002EF08 dq 1.0;数据外部参照:VarDecFromR4:loc_1002F23Ar
.文本:1002EF08;VarDecFromR8:loc_1002; f4bar。。。
.文本:1002EF10 dd 0
.文本:1002EF14 dd 402400000小时,0
.文本:1002EF1C dd 40590000h,0
.文本:1002EF24 dd 408F4000h,0
.文本:1002EF2C dd 40C38800h,0
.文本:1002EF34 dd 40F86A00h,0
.文本:1002EF3C dd 412E8480h,0
.文本:1002EF44 dd 416312D0h,0
.文本:1002EF4C dd 4197D784h,0
.文本:1002EF54 dd 41CDCDCD65H、20000000h、4202A05Fh、0E8000000h、42374876h
.文本:1002EF54 dd 0A2000000h、426D1A94h、0E5400000h、42A2309Ch、1E900000h
.文本:1002EF54 dd 42D6BCC4h、26340000h、430C6BF5h、37E08000h、4341C379h
.文本:1002EF54 dd 85D8A000h、43763457h、674EC800h、43ABC16Dh、60913D00h
.文本:1002EF54 dd 43E158E4h、78B58C40h、4415AF1Dh、0D6E2EF50h、444B1AE4h
.文本:1002EF54 dd 64DD592h、4480F0CFh、0C7E14AF6h、44B52D02h、79D99DB4h
.文本:1002EF54 dd 44EA7843h、2C280291h、45208B2Ah、0B7320335h、4554ADF4h
.文本:1002EF54 dd 0E4FE8402h、4589D971h、2F1F11281H、45C027E7h、0FAE6D721h
.文本:1002EF54 dd 45F431E0h、39A08CEAh、46293E59h、8808B024h、465F8DEFh
.文本:1002EF54 dd 0B5056E17h、4693B8B5h、2246C99Ch、46C8A6E3h、0EAD87C03h
.文本:1002EF54 dd 46FED09Bh、72C74D82h、47334261h、0CF7920E3h、476812F9h
.文本:1002EF54 dd 4357691Bh、479E17B8h、2A16A1B1h、47D2CED3h、0F49C4A1Dh
.文本:1002EF54 dd 48078287h、0F1C35CA5h、483D6329h、371A19E7h、48725DFAh
.文本:1002EF54 dd 0C4E0A061h、48A6F578h、0F618C879h、48DCB2D6h、59CF7D4Ch
.文本:1002EF54 dd 4911EFC6h、0F0435C9Eh、49466BB7h、0EC5433C6h、497C06A5h
.文本:1002EF54 dd 0B3B4A05Ch、49B18427h、0A0A1C873h、49E531H、8CA3A8Fh
.文本:1002EF54 dd 4A1B5E7Eh、0C57E649Ah、4A511B0Eh、76DDFDC0h、4A8561D2h
.文本:1002EF54 dd 14957D30h、4ABABA47h、6CDD6E3Eh、4AF0B46Ch、8814C9CEh
.文本:1002EF54 dd 4B24E187h、6A19FC41h、4B5A19E9h、0E2503DA9h、4B905031h
.文本:1002EF54 dd 5AE44D13h、4BC4643Eh、0F19D6057h、4BF97D4Dh、6E04B86Dh
.文本:1002EF54 dd 4C2FDCA1h、0E4C2F344h、4C63E9E4h、1DF3B015h、4C98E45Eh
.文本:1002EF54 dd 0A5709C1Bh、4CCF1D75h、87666191h
[System.Security.SecuritySafeCritical]  // auto-generated
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern double ToDouble(Decimal d);\
FCIMPL1(double, COMDecimal::ToDouble, FC_DECIMAL d)
{
    FCALL_CONTRACT;

    ENSURE_OLEAUT32_LOADED();

    double result = 0.0;
    // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0
    VarR8FromDec(&d, &result);
    return result;
}
FCIMPLEND
HRESULT __stdcall VarR8FromDec(const DECIMAL *pdecIn, DOUBLE *pdblOut)
{
  BYTE v2; // dl@1
  BYTE v3; // bl@2
  double v4; // st7@4
  HRESULT result; // eax@8

  v2 = pdecIn->scale;
  if ( v2 > 0x1Cu || (v3 = pdecIn->sign, v3 & 0x7F) )
  {
    result = -2147024809;
  }
  else
  {
    if ( (pdecIn->Mid32 & 0x80000000u) == 0 )
      v4 = ((double)pdecIn->Hi32 * 1.844674407370955e19 + (double)*(signed __int64 *)&pdecIn->Lo32) / sub_1006AC0D(v2);
    else
      v4 = ((double)*(signed __int64 *)&pdecIn->Lo32 + 1.844674407370955e19 + (double)pdecIn->Hi32
                                                                            * 1.844674407370955e19)
         / sub_1006AC0D(v2);
    if ( v3 )
      v4 = -v4;
    *(_QWORD *)pdblOut = *(_QWORD *)&v4;
    result = 0;
  }
  return result;
}

double __fastcall sub_1006AC0D(unsigned int a1)
{
  double result; // st7@2

  if ( a1 > 0x50 )
    result = sub_1006ABD4((void *)a1, 10.0);
  else
    result = dbl_1002EF08[a1];
  return result;
}

double __thiscall sub_1006ABD4(void *this, double a2)
{
  unsigned int v2; // eax@1
  double v3; // st6@3
  double i; // st5@3
  double result; // st7@9

  v2 = (unsigned int)this;
  if ( (signed int)this < 0 )
    v2 = -(signed int)this;
  v3 = 1.0;
  for ( i = a2; ; i = i * i )
  {
    if ( v2 & 1 )
      v3 = v3 * i;
    v2 >>= 1;
    if ( !v2 )
      break;
  }
  if ( (signed int)this >= 0 )
    result = v3;
  else
    result = 1.0 / v3;
  return result;
}

.text:1002EF08 ; double dbl_1002EF08[]
.text:1002EF08 dbl_1002EF08    dq 1.0                  ; DATA XREF: VarDecFromR4:loc_1002F23Ar
.text:1002EF08                                         ; VarDecFromR8:loc_1002F4BAr ...
.text:1002EF10                 dd 0
.text:1002EF14                 dd 40240000h, 0
.text:1002EF1C                 dd 40590000h, 0
.text:1002EF24                 dd 408F4000h, 0
.text:1002EF2C                 dd 40C38800h, 0
.text:1002EF34                 dd 40F86A00h, 0
.text:1002EF3C                 dd 412E8480h, 0
.text:1002EF44                 dd 416312D0h, 0
.text:1002EF4C                 dd 4197D784h, 0
.text:1002EF54                 dd 41CDCD65h, 20000000h, 4202A05Fh, 0E8000000h, 42374876h
.text:1002EF54                 dd 0A2000000h, 426D1A94h, 0E5400000h, 42A2309Ch, 1E900000h
.text:1002EF54                 dd 42D6BCC4h, 26340000h, 430C6BF5h, 37E08000h, 4341C379h
.text:1002EF54                 dd 85D8A000h, 43763457h, 674EC800h, 43ABC16Dh, 60913D00h
.text:1002EF54                 dd 43E158E4h, 78B58C40h, 4415AF1Dh, 0D6E2EF50h, 444B1AE4h
.text:1002EF54                 dd 64DD592h, 4480F0CFh, 0C7E14AF6h, 44B52D02h, 79D99DB4h
.text:1002EF54                 dd 44EA7843h, 2C280291h, 45208B2Ah, 0B7320335h, 4554ADF4h
.text:1002EF54                 dd 0E4FE8402h, 4589D971h, 2F1F1281h, 45C027E7h, 0FAE6D721h
.text:1002EF54                 dd 45F431E0h, 39A08CEAh, 46293E59h, 8808B024h, 465F8DEFh
.text:1002EF54                 dd 0B5056E17h, 4693B8B5h, 2246C99Ch, 46C8A6E3h, 0EAD87C03h
.text:1002EF54                 dd 46FED09Bh, 72C74D82h, 47334261h, 0CF7920E3h, 476812F9h
.text:1002EF54                 dd 4357691Bh, 479E17B8h, 2A16A1B1h, 47D2CED3h, 0F49C4A1Dh
.text:1002EF54                 dd 48078287h, 0F1C35CA5h, 483D6329h, 371A19E7h, 48725DFAh
.text:1002EF54                 dd 0C4E0A061h, 48A6F578h, 0F618C879h, 48DCB2D6h, 59CF7D4Ch
.text:1002EF54                 dd 4911EFC6h, 0F0435C9Eh, 49466BB7h, 0EC5433C6h, 497C06A5h
.text:1002EF54                 dd 0B3B4A05Ch, 49B18427h, 0A0A1C873h, 49E5E531h, 8CA3A8Fh
.text:1002EF54                 dd 4A1B5E7Eh, 0C57E649Ah, 4A511B0Eh, 76DDFDC0h, 4A8561D2h
.text:1002EF54                 dd 14957D30h, 4ABABA47h, 6CDD6E3Eh, 4AF0B46Ch, 8814C9CEh
.text:1002EF54                 dd 4B24E187h, 6A19FC41h, 4B5A19E9h, 0E2503DA9h, 4B905031h
.text:1002EF54                 dd 5AE44D13h, 4BC4643Eh, 0F19D6057h, 4BF97D4Dh, 6E04B86Dh
.text:1002EF54                 dd 4C2FDCA1h, 0E4C2F344h, 4C63E9E4h, 1DF3B015h, 4C98E45Eh
.text:1002EF54                 dd 0A5709C1Bh, 4CCF1D75h, 87666191h, 4D037269h, 0E93FF9F5h
.text:1002EF54                 dd 4D384F03h, 0E38FF872h, 4D6E62C4h, 0E39FB47h, 4DA2FDBBh
.text:1002EF54                 dd 0D1C87A19h, 4DD7BD29h, 463A989Fh, 4E0DAC74h, 0ABE49F64h
.text:1002EF54                 dd 4E428BC8h, 0D6DDC73Dh, 4E772EBAh, 8C95390Ch, 4EACFA69h
.text:1002EF54                 dd 0F7DD43A7h, 4EE21C81h, 75D49491h, 4F16A3A2h, 1349B9B5h
.text:1002EF54                 dd 4F4C4C8Bh, 0EC0E1411h, 4F81AFD6h