Odd behaviour with Decimal.ToString

O

Oenone

Can anyone explain why the following happens?

\\\
Dim d1 As Decimal = CDec("100")
Dim d2 As Decimal = CDec("100.00")

MsgBox(d1.ToString) 'displays "100"
MsgBox(d2.ToString) 'displays "100.00"

MsgBox(d1 = d2) 'displays "True"
///

How are the Decimal variables remembering the fact that I provided the two
decimal places for d2, but not for d1? I can't see any properties of the
Decimal structure that would allow it to do this...

Thanks,
 
P

Peter Proost

I think it has to do with having . set as decimal seperator and , grouping
symbol in your regional settings
Because with me 100 = 100.00 = false
but 100 = 100,00 = true because I've my decimal seperator = , and grouping
symbol = .
If I set . as decimal seperator and , as grouping symbol then 100 = 100.00
= true
and 100 = 100,00 = false

hth

Greetz Peter
 
O

Oenone

Peter said:
I think it has to do with having . set as decimal seperator and ,
grouping symbol in your regional settings

Ah sorry, I should have been clearer with that -- I do indeed have "." as
decimal and "," as thousands separators (UK regional settings).

But that wasn't the question I was asking -- the question was how and why
the Decimal variable stores the fractional part of the value when all of the
fractional digits were zero. How does the Decimal variable distinguish
between me storing "100.00" and "100"? I would have expected
Decimal.ToString to return "100" in both cases, just like a Double variable
does.
 
P

Peter Proost

It seems normal behavior to me, but to the how and why I can't realy give
you an answer. To only thing I could find in the msdn help is:

Decimal.ToString Method (String) [Visual Basic]

Remarks
If format is a null reference (Nothing in Visual Basic) or an empty string,
the return value of this instance is formatted with the general format
specifier ("G").

Hth Greetz Peter
 
A

Andrew Morton

Oenone said:
How are the Decimal variables remembering the fact that I provided
the two decimal places for d2, but not for d1? I can't see any
properties of the Decimal structure that would allow it to do this...

From the help:-
"Decimal variables are stored as signed 128-bit (16-byte) integers scaled by
a variable power of 10. The scaling factor specifies the number of digits to
the right of the decimal point"

So you have a case of scaling factor being 0 or 2.

Andrew
 
L

Larry Lard

Oenone said:
Can anyone explain why the following happens?

\\\
Dim d1 As Decimal = CDec("100")
Dim d2 As Decimal = CDec("100.00")

MsgBox(d1.ToString) 'displays "100"
MsgBox(d2.ToString) 'displays "100.00"

MsgBox(d1 = d2) 'displays "True"
///

How are the Decimal variables remembering the fact that I provided the two
decimal places for d2, but not for d1? I can't see any properties of the
Decimal structure that would allow it to do this...

This was pretty interesting! For the short answer, set a breakpoint on
a msgbox and note the difference in output here (GetBits is what we use
when we find that BitConverter.GetBytes doesn't accept a Decimal...)

?system.decimal.getbits(d1)
{Length=4}
(0): 100
(1): 0
(2): 0
(3): 0
?system.decimal.getbits(d2)
{Length=4}
(0): 10000
(1): 0
(2): 0
(3): 131072

Now the reason .Equals (which is what = gets compiled to) returns true
is because these two values do both represent the same value (exactly
100) but as you can see, internally they have different
representations!
From the help:
A decimal number is a signed, fixed-point value consisting of an
integral part and an optional fractional part. The integral and
fractional parts consist of a series of digits that range from zero to
nine (0 to 9), separated by a decimal point symbol.

The binary representation of an instance of Decimal consists of a 1-bit
sign, a 96-bit integer number, and a scaling factor used to divide the
96-bit integer and specify what portion of it is a decimal fraction.
The scaling factor is implicitly the number 10, raised to an exponent
ranging from 0 to 28.
So without poking around too much, we can surmise that CDec("100") gets
you a decimal which is stored as 100 * 10^0, but CDec("100.00") gets
you a decimal which is stored as 10000 * 10^-2. And a plain vanilla
..ToString just dumps out the internal representation.

The lesson? Always specify a format for outputting Decimals, I suppose.
 
O

Oenone

Larry said:
This was pretty interesting!

Thanks very much for your detailed reply, always nice to find more about the
internal workings of VB.NET.
The lesson? Always specify a format for outputting Decimals, I
suppose.

The code I was trying to write was a generic piece of code that builds XML,
and ultimately compares the XML as a string against another piece of XML, so
I couldn't put any formatting into the ToString() call as I didn't know what
format to use. I resolved it by converting the Decimal to a Double, and then
calling the ToString() method on that. That returns the numbers with no
unnecessary decimal places regardless of how the value was originally
specified.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top