Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double vs Decimal Rounding in C#

Why does:

double dividend = 1.0;
double divisor = 3.0;
Console.WriteLine(dividend / divisor * divisor);

output 1.0,

but:

decimal dividend = 1;
decimal divisor = 3;
Console.WriteLine(dividend / divisor * divisor);

outputs 0.9999999999999999999999999999

?

I understand that 1/3 can't be computed exactly, so there must be some rounding. But why does Double round the answer to 1.0, but Decimal does not?

Also, why does double compute 1.0/3.0 to be 0.33333333333333331? If rounding is used, then wouldn't the last 3 get rounded to 0, why 1?

like image 818
David Klempfner Avatar asked Mar 11 '13 04:03

David Klempfner


People also ask

Should I use decimal or double?

If numbers must add up correctly or balance, use decimal. This includes any financial storage or calculations, scores, or other numbers that people might do by hand. If the exact value of numbers is not important, use double for speed.

Can double have decimals in C?

Difference between float and double in C/C++ In terms of number of precision it can be stated as double has 64 bit precision for floating point number (1 bit for the sign, 11 bits for the exponent, and 52* bits for the value), i.e. double has 15 decimal digits of precision.

What is the difference between decimal and double?

Double uses 64 bits to represent data. Decimal uses 128 bits to represent data.

Is double data type same as decimal?

The Double data type is faster and requires less memory, but it is subject to rounding errors. The Decimal data type retains complete accuracy to 28 decimal places. Floating-point ( Single and Double ) numbers have larger ranges than Decimal numbers but can be subject to rounding errors.


1 Answers

Why 1/3 as a double is 0.33333333333333331

The closest way to represent 1/3 in binary is like this: 0.0101010101... That's the same as the series 1/4 + (1/4)^2 + (1/4)^3 + (1/4)^4...

Of course, this is limited by the number of bits you can store in a double. A double is 64 bits, but one of those is the sign bit and another 11 represent the exponent (think of it like scientific notation, but in binary). So the rest, which is called the mantissa or significand is 52 bits. Assume a 1 to start and then use two bits for each subsequent power of 1/4. That means you can store: 1/4 + 1/4^2 + ... + 1/4 ^ 27 which is 0.33333333333333331

Why multiplying by 3 rounds this to 1

So 1/3 represented in binary and limited by the size of a double is: 0.010101010101010101010101010101010101010101010101010101 I'm not saying that's how it's stored. Like I said, you store the bits starting after the 1, and you use separate bits for the exponent and the sign. But I think it's useful to consider how you'd actually write it in base 2.

Let's stick with this "mathematician's binary" representation and ignore the size limits of a double. You don't have to do it this way, but I find it convenient. If we want to take this approximation for 1/3 and multiply by 3, that's the same as bit shifting to multiply by 2 and then adding what you started with. This gives us 1/3 * 3 = 0.111111111111111111111111111111111111111111111111111111

But can a double store that? No, remember, you can only have 52 bits of mantissa after the first 1, and that number has 54 ones. So we know that it'll be rounded, in this case rounded up to exactly 1.

Why for decimal you get 0.9999999999999999999999999999

With decimal, you get 96 bits to represent an integer, with additional bits representing the exponent up to 28 powers of 10. So even though ultimately it's all stored as binary, here we're working with powers of 10 so it makes sense to think of the number in base 10. 96 bits lets us express up to 79,228,162,514,264,337,593,543,950,335, but to represent 1/3 we're going to go with all 3's, up to the 28 of them that we can shift to the right of the decimal point: 0.3333333333333333333333333333.

Multiplying this approximation for 1/3 by 3 gives us a number we can represent exactly. It's just 28 9's, all shifted to the right of the decimal point: 0.9999999999999999999999999999. So unlike with double's there's not a second round of rounding at this point.

like image 147
Tim Goodman Avatar answered Oct 04 '22 00:10

Tim Goodman