tags:

views:

81

answers:

3

I was playing with decimal today. I noticed this:

Decimal.MaxValue 
79228162514264337593543950335

Decimal.MaxValue - 0.5m
79228162514264337593543950334

The following code prints true.

static void Main(string[] args)
        {
            decimal d = Decimal.MaxValue - 0.5M;
            var b = d % 1 == 0;
            Console.WriteLine(b);

        }

I am sure there is a reason behind this but I don't know what it is.

A: 

It is not an integer, it is in fact a Decimal.

Integers (Int32) can have values from negative 2,147,483,648 positive 2,147,483,647.

As you can see, this is well out of these ranges.

In term of the display and accuracy, 79228162514264337593543950334.5 cannot be represented by Decimal.

Read this for more details (What Every Computer Scientist Should Know About Floating-Point Arithmetic).

Oded
Integer and `int`/`Int32` aren't necessarily synonyms.
LukeH
-1 because following code prints True.decimal d = Decimal.MaxValue - 0.5M;b = d % 1 == 0;Console.WriteLine(b);
chikak
@chikak - That doesn't make it an integer. `var d = decimal.MaxValue - 0.5M; Console.WriteLine(typeof(decimal).ToString());` prints out `System.Decimal`.
Oded
@Oded, @chikak: You're arguing about semantics. An integer is a whole number. The result in this case is an integer but it's stored in a `decimal` variable. If I type `decimal foo = 9876543210;` then `foo` is a `decimal`, but that doesn't magically mean that `9876543210` is no longer an integer.
LukeH
I know it is a decimal and can't fit into an int variable. What I meant was that the result should have a decimal point in it. Why can't 79228162514264337593543950334.5 fit into a decimal? I am reading the link you gave but it is a bit long document :-)
chikak
@LukeH I agree with your comment.
chikak
A: 

0.5 is being rounded before subtraction. decimal strives to make the result as precise as possible, so the operation becomes 79228162514264337593543950335 - 00000000000000000000000000000.5. But 0.5 cannot be represented as a decimal of the required precision and is rounded upwards to 1.

Anton Tykhyy
The decimal type is perfectly able to represent 0.5m.
Bevan
I said "0.5 cannot be represented as a decimal of the required precision", which is different from "0.5 cannot be represented as a decimal".
Anton Tykhyy
+4  A: 

The decimal type uses 96 bits to store the sequence of digits (ref), plus a sign (1 bit) and a scaling factor that specifies the location of the decimal place.

For this decimal number:

79228162514264337593543950335

All 96 bits are used to the left of the decimal point - there's nothing left to represent the fractional part of the answer. So, it gets rounded.

If you divide the number by 10:

7922816251426433759354395033.5

Then you have a few bits available to represent the fractional part - but only to 1/10, no finer.

The key difference between decimal and double/float is that it is based on a decimal scaling factor specifying the location of a decimal point; the other floating types are based on a binary scaling factor specifying the location of a binary point.

Bevan
If you were right about the result being rounded instead of the operands, the result would be 79228162514264337593543950335 not 79228162514264337593543950334, because 79228162514264337593543950334.5 rounds upwards.
Anton Tykhyy
@Anton Good catch :-)
chikak
I did some calculation myself and this answer sounds correct.
chikak
@Anton - the `decimal` type is primarily intended for handling amounts of money/currency, one area where people tend to notice the flaws of double/float. For that reason, `decimal` uses *Bankers Rounding* (see http://en.wikipedia.org/wiki/Rounding). Why? Always rounding 0.5 up gives a statistical bias that causes a small amount of overstating, especially significant when totalling a column of rounded values. Bankers rounding avoids this by rounding 0.5 to the *nearest even value*. So, 3.5 rounds to 4.0; 4.5 also rounds to 4.0.
Bevan
Thanks, I didn't know that)
Anton Tykhyy