• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 227
  • Last Modified:

For Next loop not counting correctly

Hi Experts,

When I execute the attached code the loop does not count from 1 to 2 in the expected way.

1
1.1
...
1.9
2.0

Instead it doe something like this

1
1.1
1.2000000000000002
...
1.9000000000000008
(never reaches 2)

I am sure it is because binary can not store the number 0.1 accurately so what is the accepted way to count this loop.

For LoopCount As Double = 1 To 2 Step 0.1
   ListBox1.Items.Add(LoopCount)
Next

Open in new window

0
DColin
Asked:
DColin
  • 5
  • 5
  • 2
  • +1
1 Solution
 
ladarlingCommented:
Your double is being cast to 'Object' in the add() method, which is causing 'widening' of your number. Assert the Double to Decimal at the last second to preserve the narrowing during the cast to object.
Try:
ListBox1.Items.Add(CDec(LoopCount))
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
The ToString() method is being implicitly called on the resulting decimal in ladarling's above code:

    ListBox1.Items.Add(CDec(LoopCount))     ----->     ListBox1.Items.Add(CDec(LoopCount).ToString)

You could just call it directly like this:

    For LoopCount As Double = 1 To 2 Step 0.1
        ListBox1.Items.Add(LoopCount.ToString)
    Next

0
 
samtran0331Commented:
Another option is to use FormatNumber which also returns a string, but there are a lot of options as far as how to...well...format the number....

Also, to have 2.0 in the list, you'd actually have to step up to 2.1....

        For LoopCount As Double = 1 To 2.1 Step 0.1
            ListBox1.Items.Add(FormatNumber(LoopCount, 1))
        Next
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Or go from 10 to 20 instead and display it with the formatting in ToString() itself:

        For LoopCount As Integer = 10 To 20
            ListBox1.Items.Add(CDbl(LoopCount / 10).ToString("0.0"))
        Next
0
 
ladarlingCommented:
Idle_Mind said: The ToString() method is being implicitly called on the resulting decimal in ladarling's above code.
Why makes you say that? Casting to the 'Object' type does not imply a conversion of any kind, since all types widen to object. And conversion from double to decimal types is explicit and narrowing within the numeric types, and also does not imply a string conversion.
http://msdn.microsoft.com/en-us/library/k1e94s7e(VS.80).aspx

ListBox items are type Object, so I am not confused by your assertion...
0
 
ladarlingCommented:
And by not confused, I me 'confused", lol. Confusing, eh?
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
The act of converting to decimal with CDec() doesn't call ToString()...on that you are correct.  =)

It is the ListBox itself that is calling ToString() on your Decimal object when it displays it on the GUI for you.

See: http://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.objectcollection.add.aspx

    "When an object is added to the collection, the ListBox first checks to see if the DisplayMember property of the ListControl class has the name of a member from the object specified to reference when obtaining the item text. If the DisplayMember property does not have a member specified, the ListBox then calls the ToString method of the object to obtain the text to display in the list."

It is the ToString() call (that is automatically being done for you) that actually causes the proper display of the value...not the CDec() conversion.

This is evidenced by the snippet I posted:

    For LoopCount As Double = 1 To 2 Step 0.1
        ListBox1.Items.Add(LoopCount.ToString)
    Next

It resulted in the output below.  Note that NO conversion to Decimal was necessary at all...  =)


ListBox.jpg
0
 
ladarlingCommented:
Idle_Mind: It resulted in the output below.  Note that NO conversion to Decimal was necessary at all...  =)
Everything you posted makes perfect sense, I like it, except that if the ListBox is intrinsically calling the objects ToString() method, why is the author seeing this problem (and thus posting the question =-) on Double type variables?
So.. there seems to be something else going on with the listbox control. Any idea what it is, Idle. If you know the answer, dont tell me, I will post it for points :-)
-----------------------------------------------
I would point out to the author, by the way, that if you use a Decimal for LoopCount in the first place, all of our discussion here is moot. It works like a champ (and even actually displays 2.0). Right now I just want to pick Idls' brain on this.
 
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
A perplexing problem...    *** knocks head on table ***

The ListBox.Items.Add() method expects Objects...thus we get a widening conversion on the Double to Object when it is stored in the ListBox.

It is still a Double though as the code below shows.

(Immediate Window Output)
Double : 1
Double : 1.1
Double : 1.2
Double : 1.3
Double : 1.4
Double : 1.5
Double : 1.6
Double : 1.7
Double : 1.8
Double : 1.9

But the ListBox shows it differently so as you said, "...there seems to be something else going on with the listbox control.".

To be honest, I really don't know why the ListBox's ToString() call results in different output than calling ToString() directly on the Double...or even iterating the Items() collection manually calling ToString() on each item.

I tried many combinations, converting to Object and then back to Double...I even tried displaying the values using ToString() with System.Globalization.CultureInfo.CurrentCulture and System.Globalization.CultureInfo.InvariantCulture but still couldn't reproduce the ListBoxes output.

I did find that setting the ListBox.FormatString() property to "N1" results in the desired output...not really an answer to the underlying question...just found it interesting.

So please enlighten us with a TECHNICAL answer on why the ListBox output is different.

I love learning new things.  ;)


Public Class Form1
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For LoopCount As Double = 1 To 2 Step 0.1
            ListBox1.Items.Add(LoopCount)
        Next
    End Sub
 
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For Each O As Object In ListBox1.Items
            Debug.Print(TypeName(O) & " : " & O)
        Next
    End Sub
 
End Class

Open in new window

ListBox.jpg
0
 
DColinAuthor Commented:
Hi Experts,

My problem is not with the list box display but the fact that this loop will not count to 2. I thought this is because binary can not store the number 0.1 so the loop does not count
1
1.1
1.2
...
1.9
2.0
But instead counts as closely to binary as the double's precision will allow. This means that the last two counts should be.
1.9
2.0
But due to the precision limitations of binary are actually.
1.900000000000008
2.000000000000009
And therefore the loop count will not go past 2 and so halts at 1.9000000000008. If this is the case how do I get around this problem using the accepted methods. VB.Net does not have a Binary Coded Decimal number type.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
In ladarlings last comment he stated:

    "I would point out to the author, by the way, that if you use a Decimal for LoopCount in the first place, all of our discussion here is moot. It works like a champ (and even actually displays 2.0)."

So change your variable from Double to Decimal.
0
 
DColinAuthor Commented:
Thank you Idle Mind I missed ladarling's Decimal advice. It would seem VB.Net has a BCD data type after all.
0
 
ladarlingCommented:
Idle: So please enlighten us with a TECHNICAL answer on why the ListBox output is different.

Chris: I have no Idea! I was hoping you would...lol. I did the same kind of thing you did, experimenting with debug output, etc, and could not figure it out. My *theory* is that the Listbox's double>object conversion operator is busted. I guess I could start digging through the ListBox code with the MSIL disassembler, but that sounds like tedious, tedious work (to which I am strongly opposed :-)
 But, I will post it for further exploration. Maybe someone out there has the technical solution, not just a theory. I dont. :-)
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 5
  • 5
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now