One possible solution is to decompose d as the exact sum of n doubles, the last of which is small and contains all the final significant digits you want when converting to decimal, and the first (n-1) that will be converted exactly to decimal.
For a double d source 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 at ideone.com.
Note that d -= operations d -= accurate even if C # calculates binary floating point operations with higher precision than double (which allows itself to be done).
This is cheaper than converting from double to string, and gives a few extra digits of accuracy for the result (four precision bits for the four previous if-then-elses).
Note: if C # did not allow itself to perform floating point calculations with higher precision, a good trick would be to use Decker separation to separate d into two values d1 and d2 , which convert each to exactly decimal. Alas, Decker splitting only works with a strict interpretation of the multiplication and addition of IEEE 754.
Another idea is to use the C # frexp version to get the s value and the exponent d , and also calculate (Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal> .
Pascal cuoq
source share