Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding a Single

Tags:

c#

.net

I have this "scientific application" in which a Single value should be rounded before presenting it in the UI. According to this MSDN article, due to "loss of precision" the Math.Round(Double, Int32) method will sometimes behave "unexpectedly", e.g. rounding 2.135 to 2.13 rather than 2.14.

As I understand it, this issue is not related to "banker's rounding" (see for example this question).

In the application, someone apparently chose to address this issue by explicitly converting the Single to a Decimal before rounding (i.e. Math.Round((Decimal)mySingle, 2)) to call the Math.Round(Decimal, Int32) overload instead. Aside from binary-to-decimal conversion issues possibly arising, this "solution" may also cause an OverflowException to be thrown if the Single value is too small or to large to fit the Decimal type.

Catching such errors to return the result from Math.Round(Double, Int32), should the conversion fail, does not strike me as the perfect solution. Nor does rewriting the application to use Decimal all the way.

Is there a more or less "correct" way to deal with this situation, and if so, what might it be?

like image 444
Oskar Lindberg Avatar asked Jun 25 '15 14:06

Oskar Lindberg


2 Answers

I would argue that your existing solution (using the Decimal version of Math.Round) is the correct one.

The underlying problem is that you expect numbers to be rounded according to their base 10 representation, but you've stored them as base 2 floating point numbers. The provided example of 2.135 is one of those edge cases where the base 2 representation doesn't exactly match the base 10.

To get the expected rounding behavior, you must convert the numbers to base 10. The easiest way is exactly what you're already doing: temporarily convert the number to a Decimal long enough to call Math.Round.

like image 81
RogerN Avatar answered Oct 12 '22 04:10

RogerN


Since floating point trades precision for range, the decimal value 2.135 can't be exactly represented in binary.

The [closest] binary representation works out to be something like 0.1348876953125 decimal, so the rounding is correct (if not intuitively obvious).

You should read Goldberg's paper, "What every computer scientist should know about floating-point arithmetic" (ACM Computing Surveys, Volume 23 Issue 1, March 1991, pp. 5-48)

Abstract. Floating-point arithmetic is considered as esoteric subject by many people. This is rather surprising, because floating-point is ubiquitous in computer systems: Almost every language has a floating-point datatype; computers from PCs to supercomputers have floating-point accelerators; most compilers will be called upon to compile floating-point algorithms from time to time; and virtually every operating system must respond to floating-point exceptions such as overflow. This paper presents a tutorial on the aspects of floating-point that have a direct impact on designers of computer systems. It begins with background on floating-point representation and rounding error, continues with a discussion of the IEEE floating point standard, and concludes with examples of how computer system builders can better support floating point.

like image 23
Nicholas Carey Avatar answered Oct 12 '22 04:10

Nicholas Carey