【问题标题】:Extracting mantissa and exponent from double in c#从C#中的double中提取尾数和指数
【发布时间】:2010-09-28 05:56:12
【问题描述】:

是否有任何直接的方法可以从 c#(或一般的 .NET)中的双精度数获取尾数和指数?

我使用 Google 找到了this example,但我不确定它的强大程度。可以在某些未来版本的框架等中进行双重更改的二进制表示吗?

我发现的另一种选择是使用 System.Decimal 而不是 double 并使用 Decimal.GetBits() 方法来提取它们。

有什么建议吗?

【问题讨论】:

    标签: c# .net floating-point


    【解决方案1】:

    我可以想到另一种方法,使用double.ToString("E17") 并解析生成的科学记数法字符串。但是如果双精度数的有效数字太多,这种方法对于尾数提取会不准确。

    public class DoubleParser
    {
        public static int GetExponent(double d)
        {
            var doubleParts = ExtractScientificNotationParts(d);
            return Convert.ToInt32(doubleParts[1]);
        }
    
        public static double GetMantissa(double d)
        {
            var doubleParts = ExtractScientificNotationParts(d);
            return Convert.ToDouble(doubleParts[0]);
        }
    
        private static string[] ExtractScientificNotationParts(double d)
        {
            var doubleParts = d.ToString(@"E17").Split('E');
            if (doubleParts.Length != 2)
                throw new ArgumentException();
    
            return doubleParts;
        }
    }
    

    像这样使用上面的类:

    var value1 = 43948530544.3433;
    var mantissa = DoubleParser.GetMantissa(value1);  // 4.39485305443433
    var exponent = DoubleParser.GetExponent(value1);  // 10
    
    var value2 = 0.00000000009869232667160136;
    mantissa = DoubleParser.GetMantissa(value2);  // 9.8692326671601371
    exponent = DoubleParser.GetExponent(value2);  // -11
    

    【讨论】:

      【解决方案2】:

      二进制格式不应该改变——它肯定会是对现有规范的重大改变。正如 Jimmy 所说,它被定义为 IEEE754 / IEC 60559:1989 格式。 (C# 3.0 语言规范第 1.3 节;ECMA 335 第 8.2.2 节)。 DoubleConverter 中的代码应该很好且健壮。

      为了方便以后参考,示例中代码的相关位是:

      public static string ToExactString (double d)
      {
          …
      
          // Translate the double into sign, exponent and mantissa.
          long bits = BitConverter.DoubleToInt64Bits(d);
          // Note that the shift is sign-extended, hence the test against -1 not 1
          bool negative = (bits & (1L << 63)) != 0;
          int exponent = (int) ((bits >> 52) & 0x7ffL);
          long mantissa = bits & 0xfffffffffffffL;
      
          // Subnormal numbers; exponent is effectively one higher,
          // but there's no extra normalisation bit in the mantissa
          if (exponent==0)
          {
              exponent++;
          }
          // Normal numbers; leave exponent as it is but add extra
          // bit to the front of the mantissa
          else
          {
              mantissa = mantissa | (1L << 52);
          }
      
          // Bias the exponent. It's actually biased by 1023, but we're
          // treating the mantissa as m.0 rather than 0.m, so we need
          // to subtract another 52 from it.
          exponent -= 1075;
      
          if (mantissa == 0) 
          {
              return negative ? "-0" : "0";
          }
      
          /* Normalize */
          while((mantissa & 1) == 0) 
          {    /*  i.e., Mantissa is even */
              mantissa >>= 1;
              exponent++;
          }
      
          …
      }
      

      当时这些 cmets 对我来说是有意义的,但我相信我现在必须考虑一段时间。在第一部分之后,您已经获得了“原始”指数和尾数 - 其余代码只是有助于以更简单的方式处理它们。

      【讨论】:

      • 如果我正确理解规范,格式有两种形式,一种是规范化版本,一种是非规范化版本。大部分代码处理将非规范化尾数/指数转换为规范化形式
      • 我没有得到“减去另一个 52”部分。那里发生了什么?
      • @Jay R:老实说,这有点难以解释——这条评论的效果与我能想到的一样好。我想举个例子最容易,但我现在恐怕我的大脑已经筋疲力尽了:(
      • 现在我已经考虑了更多,我得到了 1075. 1023 加上 52 个班次,将尾数从一个非常大的数字 * 2^exponent 变为一个小数字。
      • @Michael:我怀疑你对待指数的方式与我相反,仅此而已。 (左移与右移。)
      【解决方案3】:

      表示是 IEEE 标准,不应更改。

      https://msdn.microsoft.com/en-us/library/system.double(v=vs.110).aspx

      Double 类型符合二进制浮点运算的 IEC 60559:1989 (IEEE 754) 标准。

      编辑:decimal 有 getBits 而 double 没有的原因是 decimal 保留了有效数字。 3.0000m == 3.00m 但指数/尾数实际上是不同的。我认为浮点数/双精度数是独一无二的。

      【讨论】:

      • 在 C/C++ 中双精度不一定必须是 64 位。我在几个 32 位(即与浮点数相同)的系统上工作过。 C# 有什么不同吗?
      • 是的,它在规范中定义为 64 位。这是关于 C/C++ 中的 double (和 long/int/long long )惨败的一个好点
      • @Jimmy 当(或如果)C# 至少支持 C/C++ 支持的一半平台时,您可以谈谈 C/C++ 中的惨败。
      • 链接需要更新。
      • 编辑队列已满,所以我将更新的链接放在这里:docs.microsoft.com/en-us/dotnet/api/system.double
      猜你喜欢
      • 1970-01-01
      • 2012-03-12
      • 1970-01-01
      • 1970-01-01
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多