问题
Is it safe to cast from double to decimal in the following manner in C#:
int downtimeMinutes = 90;
TimeSpan duration = TimeSpan.FromHours(2d);
decimal calculatedDowntimePercent = duration.TotalMinutes > 0?
(downtimeMinutes / (decimal)duration.TotalMinutes) * 100.0m : 0.0m;
If the answer is yes, then no fuss, I'll just mark as accepted.
回答1:
Yes it is safe, because decimal has greater precision
http://msdn.microsoft.com/en-us/library/364x0z75(VS.80).aspx
The compiler will put in casts around the other non decimal numbers, but they'll all fit into decimal * (see caveat).
-- Caveat
Decimal is not a floating point type. Its mandate is to always uphold precision. Whereas a floating point number such as double (which I mostly use) makes a tradeoff on precision to accommodate very large numbers). Very large or very small numbers will not fit into decimal. So Lisa needs to ask herself if the magnitude of the operation is likely to be less than 28 significant digital digits. 28 significant digits are adequate for most scenarios.
Floating point is good for astronomically large or infintessimally small numbers... or operations inbetween that yield enough accuracy. I should look this up, but double is okay for plus or minus a few billion with accuracy of up to several decimal points (up to 7 or 8?).
in the sciences there's no point measuring beyond the accuracy of your equipment. In finance, often the logical choice is double because a double is computationally more efficient for most situations (sometimes they want a bit more accuracy, but the efficiency is not worth throwing away for something like decimal). In the end we all have to get pragmatic and map business needs to a digital domain. There are tools out there that have a dynamic number representation. Probably there are libraries in .net for the same. However, is it worth it? Sometimes it is. Often it's overkill.
回答2:
In general, double
-> decimal
conversions aren't safe, because decimal
has a smaller range.
However, as long as TotalMinutes
is less than the maximum decimal
value* it will be fine. This is true, because TimeSpan.MaxValue.TotalMinutes < (double)decimal.MaxValue
(I believe TimeSpan
uses a long
internally.)
So: yes.
*: (79,228,162,514,264,337,593,543,950,335 minutes is 1.1×10^13 times the age of the universe)
回答3:
No, in general, casting from double to decimal is not always safe:
[TestCase(double.MinValue)]
[TestCase(double.MaxValue)]
[TestCase(double.NaN)]
[TestCase(double.NegativeInfinity)]
[TestCase(double.PositiveInfinity)]
public void WillFail(double input)
{
decimal result = (decimal)input; // Throws OverflowException!
}
As OP clarified in a comment to the question, "safe" being "doesn't cause run time exceptions", the above shows that exceptions can occur when casting a double to a decimal.
The above is the generic answer many Googlers might've come here for. However, to also answer the specific question by OP, here's a strong indication that the code will not throw exceptions, even on edge cases:
[Test]
public void SpecificCodeFromOP_WillNotFail_NotEvenOnEdgeCases()
{
int downtimeMinutes = 90;
foreach (TimeSpan duration in new[] {
TimeSpan.FromHours(2d), // From OP
TimeSpan.MinValue,
TimeSpan.Zero,
TimeSpan.MaxValue })
{
decimal calculatedDowntimePercent = duration.TotalMinutes > 0 ?
(downtimeMinutes / (decimal)duration.TotalMinutes) * 100.0m : 0.0m;
}
}
来源:https://stackoverflow.com/questions/6839369/automatically-cast-from-double-to-decimal-safely-is-the-following-safe