32

Is there any straightforward way to get the mantissa and exponent from a double in c# (or .NET in general)?

I found this example using Google, but I'm not sure how robust it would be. Could the binary representation for a double change in some future version of the framework, etc?

The other alternative I found was to use System.Decimal instead of double and use the Decimal.GetBits() method to extract them.

Any suggestions?

pogosama
  • 1,596
  • 20
  • 27
dvenema
  • 515
  • 1
  • 4
  • 8

3 Answers3

35

The binary format shouldn't change - it would certainly be a breaking change to existing specifications. It's defined to be in IEEE754 / IEC 60559:1989 format, as Jimmy said. (C# 3.0 language spec section 1.3; ECMA 335 section 8.2.2). The code in DoubleConverter should be fine and robust.

For the sake of future reference, the relevant bit of the code in the example is:

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++;
    }

    …
}

The comments made sense to me at the time, but I'm sure I'd have to think for a while about them now. After the very first part you've got the "raw" exponent and mantissa - the rest of the code just helps to treat them in a simpler fashion.

Jon Skeet
  • 1,335,956
  • 823
  • 8,931
  • 9,049
  • If I understand the spec correctly, there are two forms of the format, a normalized version and an un-normalized version. the bulk of the code handles converting the unnormalized mantissa/exponent to normalized form – Jimmy Dec 23 '08 at 21:06
  • I don't get the "subtract another 52" part. What's going on there? – Jay R. Oct 05 '10 at 17:59
  • @Jay R: It's slightly hard to explain, to be honest - the comment does about as good a job as I can think of. I guess it's easiest to give an example, but I'm afraid my brain is frazzled at the moment :( – Jon Skeet Oct 05 '10 at 18:06
  • Now that I've thought more about it, I get the 1075. 1023 plus 52 shifts to bring the mantissa from a very large number * 2^exponent to a small number. – Jay R. Oct 05 '10 at 22:47
  • @JonSkeet Could you at some time check out my question here ( http://stackoverflow.com/questions/17829139/mantissa-normalization-of-c-sharp-double )? I'm currently using part of this code and seem to be missing something. – Daniel Jul 24 '13 at 11:49
  • if someone can post similar code to convert `decimal` to mantissa and exponent? I guess `Decimal.GetBits()` method should be used. – Oleg Vazhnev Oct 09 '13 at 12:16
  • upd: I found here an example which calculates mantissa, but exponent is not calculated http://stackoverflow.com/a/764102/93647 – Oleg Vazhnev Oct 09 '13 at 12:22
  • @javapowered: The code in this answer *does* calculate the exponent and mantissa. It's not clear what else you'd need. – Jon Skeet Oct 09 '13 at 12:40
  • @JonSkeet right I was talking about another question http://stackoverflow.com/questions/763942/calculate-system-decimal-precision-and-scale/764102#764102. I asked in this question because when I posted my first comments I not found this, another, "decimal" question. – Oleg Vazhnev Oct 09 '13 at 13:24
  • Why is a single-character string with `0` text returned under a specific condition?  I'm attempting to use your implementation in my project but am unsure of the larger context of this algorithm.  I'm assuming a function with `out` args for `mantissa` and `exponent`, but that sole string return is throwing off my understanding. – Slipp D. Thompson Oct 02 '14 at 22:09
  • @SlippD.Thompson: If the mantissa isn't 0, there will be at least one non-zero digit. I *suspect* that if I removed that special case, it would end up returning the empty string instead of "0" if you passed in 0. – Jon Skeet Oct 03 '14 at 05:45
  • @JonSkeet I'm not completely following why strings are involved in the first place.  Or rather, a string.  There are no other `return`s nor `string`s in the code sample. – Slipp D. Thompson Oct 03 '14 at 19:15
  • @SlippD.Thompson: The link in the question is to my `DoubleConverter` code - whose purpose is to convert a `double` into its exact string representation. Follow the link for the complete code. – Jon Skeet Oct 03 '14 at 19:27
  • @JonSkeet I see now; you quoted a portion of the code the OP was unsure about.  I'll admit I skimmed the english here and went straight to the code.  ;-/  In the context of an SO answer, it wouldn't hurt to have a few extra lines of context. // Actually, I'll just do it.  I hope you don't mind. – Slipp D. Thompson Oct 03 '14 at 22:35
  • @JonSkeet: Your check for the sign `bits < 0` is incorrect for the double value `-0`. You should, imho, instead check for `(bits & (1<<63)) > 0`. – D.R. Sep 21 '18 at 17:12
  • @D.R.: Thank you, yes. Fixed. – Jon Skeet Sep 22 '18 at 08:07
  • @D.R. @JonSkeet Are those two statements not 100% equivalent? Both produce `true` for `-0.0` and `false` for `0.0`...or am I missing something here? – Mike Marynowski Dec 12 '19 at 02:59
  • I think @D.R. was thinking about the `double d` value and not the `long bits` value when he said that. If the 64th bit of a `long` is not zero then the `long` is negative so either should work fine :) – Mike Marynowski Dec 12 '19 at 03:11
  • I've run this code and I find that the correct way to "Bias the Exponent" is to do it after normalizing with the code: `exponent = 1075 - exponent;` This works correctly for the application I'm designing but I'm not sure why or if I'm making an error? – Michael Apr 08 '20 at 07:42
  • 1
    @Michael: I suspect you're treating the exponent in the opposite way to the way I am, that's all. (Shift left vs shift right.) – Jon Skeet Apr 08 '20 at 07:45
  • Thanks, yeah, this is all hazy to me but thanks for this example it was a life saver! – Michael Apr 08 '20 at 07:47
1

The representation is a IEEE standard and shouldn't change.

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

The Double type complies with the IEC 60559:1989 (IEEE 754) standard for binary floating-point arithmetic.

EDIT: The reason why decimal has a getBits and double does not is that decimal preserves significant digits. 3.0000m == 3.00m but the exponents/mantissas are actually different. I think floats/doubles are uniquely represented.

Jimmy
  • 85,597
  • 17
  • 118
  • 137
  • A double does not necessarily have to be 64-bits in C/C++. I've worked on several systems where it is 32-bits (i.e. the same as a float). Is C# different? – Judge Maygarden Dec 23 '08 at 20:33
  • 3
    yes, it is defined in the spec as 64 bits. that's a good point about the double (and long/int/long long ) fiasco in C/C++ – Jimmy Dec 23 '08 at 20:36
  • 2
    @Jimmy when (or if) C# would support at least half platforms that C/C++ support then you can say something about fiasco in C/C++. – Slava Apr 19 '13 at 17:53
  • 1
    Link needs updating. – Xonatron Oct 24 '17 at 21:28
  • Edit queue is full, so I'll put the updated link here: https://docs.microsoft.com/en-us/dotnet/api/system.double – pogosama Apr 12 '21 at 14:55
-1

I can think of another way by using double.ToString("E17") and parsing the resulting scientific notation string. But this method will be inaccurate for the mantissa extraction if the double has too many significant digits.

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;
    }
}

Use the class above like so:

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
pogosama
  • 1,596
  • 20
  • 27