33

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,818
  • 1
  • 24
  • 29
dvenema
  • 525
  • 1
  • 4
  • 8

2 Answers2

38

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,421,763
  • 867
  • 9,128
  • 9,194
  • 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
  • 89,068
  • 17
  • 119
  • 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://learn.microsoft.com/en-us/dotnet/api/system.double – pogosama Apr 12 '21 at 14:55