Double to Decimal without rounding after 15 digits
c#, decimal, double, precision
Solution
One possible solution is to decompose `d` as the exact sum of n doubles, the last of which is small and contains all the trailing significant digits that you desire when converted to decimal, and the first (n-1) of which convert exactly to decimal.
For the source double `d` between -1.0 and 1.0:
decimal t = 0M;
bool b = d < 0;
if (b) d = -d;
if (d >= 0.5) { d -= 0.5; t = 0.5M; }
if (d >= 0.25) { d -= 0.25; t += 0.25M; }
if (d >= 0.125) { d -= 0.125; t += 0.125M; }
if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; }
t += Convert.ToDecimal(d);
if (b) t = -t;
Test it on ideone.com.
Note that the operations `d -=` are exact, even if C# computes the binary floating-point operations at a higher precision than `double` (which it allows itself to do).
This is cheaper than a conversion from `double` to string, and provides a few additional digits of accuracy in the result (four bits of accuracy for the above four if-then-elses).
Remark: if C# did not allow itself to do floating-point computations at a higher precision, a good trick would have been to use Dekker splitting to split `d` into two values `d1` and `d2` that would convert each exactly to decimal. Alas, Dekker splitting only works with a strict interpretation of IEEE 754 multiplication and addition.
Another idea is to use C#'s version of frexp to obtain the significand `s` and exponent `e` of `d`, and to compute `(Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>`.
Problem
When converting a "high" precision Double to a Decimal I lose precision with Convert.ToDecimal or casting to (Decimal) due to Rounding. Example : ``` double d = -0.99999999999999956d; decimal result = Convert.ToDecimal(d); // Result = -1 decimal result = (Decimal)(d); // Result = -1 ``` The Decimal value returned by Convert.ToDecimal(double) contains a maximum of 15 significant digits. If the value parameter contains more than 15 significant digits, it is rounded using rounding to nearest. So I in order to keep my precision, I have to convert my double to a String and then call Convert.ToDecimal(String): ``` decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d ``` This method is working but I would like to avoid using a String variable in order to convert a Double to Decimal without rounding after 15 digits?