tags:

views:

1425

answers:

5

When we convert a float to integer in visual basic 6.0, how does it round off the fractional part? I am talkin about the automatic type conversion.

If we assign like

Dim i as Integer
i=5.5
msgbox i

What will it print? 5 or 6 ??

I was getting "5" a couple of months before. One day it started giving me 6! Any idea whats goin wrong? Did microsoft released some patches to fix something?

Update : 5.5 gets converted to 6 but 8.5 to 8 !

Update 2 : Adding CInt makes no difference. CInt(5.5) gives 6 and Cint(8.5) gives 8!! Kinda weired behaviour. I should try something like floor(x + 0.49);

+1  A: 

The changed behaviour sounds worrying indeed, however the correct answer surley is 6. Scroll down to "Round to even method" on Wikipedia, Rounding for an explanation.

danbystrom
+5  A: 

Update: After some googling, I came across the following article:

It is not a "bug", it is the way VB was designed to work. It uses something known as Banker's rounding which, if the number ends in exactly 5 and you want to round to the position in front of the 5, it rounds numbers down if the number in front of the 5's position is even and rounds up otherwise. It is supposed to protect against repeated calculation using rounded numbers so that answer aren't always biased upward. For more on this issue than you probably want to know, see this link

http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q196652

This explains the (apparent) weird behavior:

Cint(5.5) 'Should be 6'
Cint(8.5) 'Should be 8'

Old Update: Perhaps you should be more explicit: use CInt, instead of simply assigning a float to an integer. E.g:

Dim i as Integer
i = CInt(5.5)
MsgBox i
elo80ka
Makes no difference. CInt(5.5) gives 6 and Cint(8.5) gives 8!! Kinda weired behaviour. I this I should try something like floor(x + 0.49);
Tuxist
Okay, that _is_ weird.
elo80ka
That is normal. The type conversion functions (CInt, CLng, etc.) round before converting.
Mike Spross
A: 

The VB6 Round() function uses a Banker's Rounding method. MS KB Article 225330 (http://support.microsoft.com/kb/225330) talks about this indirectly by comparing VBA in Office 2000 to Excel's native behavior and describes it this way:

When a number with an even integer ends in .5, Visual Basic rounds the number (down) to the nearest even whole number. [...] This difference [between VBA and Excel] is only for numbers ending in a .5 and is the same with other fractional numbers.

If you need different behavior, I'm afraid you'll have to have to specify it yourself.

Mike Mustaine
+1  A: 

As others have already pointed out, the "weird behavior" you're seeing is due to the fact that VB6 uses Banker's Rounding when rounding fractional values.

Update 2 : Adding CInt makes no difference. CInt(5.5) gives 6 and Cint(8.5) gives 8!!

That is also normal. CInt always rounds (again using the Banker's Rounding method) before performing a conversion.

If you have a number with a fractional part and simply want to truncate it (ignore the portion after the decimal point), you can use either the Fix or the Int function:

Fix(1.5) = 1
Fix(300.4) = 300
Fix(-12.394) = -12

Int works the same way as Fix, except for the fact that it rounds negative numbers down to the next-lowest negative number:

Int(1.5) = 1
Int(300.4) = 300
Int(-12.394) = -13

If you actually want to round a number according to the rules most people are familiar with, you will have to write your own function to do it. Below is an example rounding that will round up when the fractional part is greater than or equal to .5, and round down otherwise:


EDIT: See MarkJ's answer for a much simpler (and probably faster) version of this function.


' Rounds value to the specified number of places'
' Probably could be optimized. I just wrote it off the top of my head,'
' but it seems to work.'
Public Function RoundNumber(ByVal value As Double, Optional PlacesAfterDecimal As Integer = 0) As Double

    Dim expandedValue As Double
    Dim returnValue As Double
    Dim bRoundUp As Boolean

    expandedValue = value
    expandedValue = expandedValue * 10 ^ (PlacesAfterDecimal + 1)
    expandedValue = Fix(expandedValue)

    bRoundUp = (Abs(expandedValue) Mod 10) >= 5

    If bRoundUp Then
        expandedValue = (Fix(expandedValue / 10) + Sgn(value)) * 10
    Else
        expandedValue = Fix(expandedValue / 10) * 10
    End If

    returnValue = expandedValue / 10 ^ (PlacesAfterDecimal + 1)
    RoundNumber = returnValue

End Function

Examples

Debug.Print RoundNumber(1.6)       '2'
Debug.Print RoundNumber(-4.8)      '-5'
Debug.Print RoundNumber(101.7)     '102'
Debug.Print RoundNumber(12.535, 2) '12.54'
Mike Spross
It's briefer and probably runs faster to use Int(0.5+value) to round to nearest whole number. And see my answer for my version of the RoundNumber function. BTW RoundNumber(-4.8) presumably should be -5 not 5
MarkJ
@MarkJ: Yeah, that's definitely simpler. I kept thinking, "wow, there must be an obvious and easy way to do this" :-) I'll refer the OP to your answer and +1 for you, good sir.
Mike Spross
Cheers! Someone showed me that trick in, ahem, I believe ZX Spectrum BASIC, sometime about 20+ years ago...
MarkJ
+6  A: 

Part of this is in the VB6 help: topic Type Conversion Functions. Unfortunately it's one of the topics that's not in the VB6 documentation on the MSDN website, but if you've installed the help with VB6, it will be there.

When the fractional part is exactly 0.5, CInt and CLng always round it to the nearest even number. For example, 0.5 rounds to 0, and 1.5 rounds to 2. CInt and CLng differ from the Fix and Int functions, which truncate, rather than round, the fractional part of a number. Also, Fix and Int always return a value of the same type as is passed in.

Implicit type coercion - a.k.a. "evil type coercion (PDF)" - from a floating point number to a whole number uses the same rounding rules as CInt and CLng. This behaviour doesn't seem to be documented anywhere in the manual.

If you want to round up when the fractional part is >= 0.5, and down otherwise, a simple way to do it is

 n = Int(x + 0.5)

And off the top of my head, here's my briefer version of Mike Spross's RoundNumber:) which is a replacement for the VB6 Round function.

 'Written off the top of my head, seems to work. 
Public Function RoundNumber(ByVal value As Double, Optional PlacesAfterDecimal As Integer = 0) As Double
  Dim nMultiplier As Long
  nMultiplier = 10 ^ PlacesAfterDecimal
  RoundNumber = Int(0.5 + value / nMultiplier) * CDbl(nMultiplier)
End Function

Sample output:

Debug.Print RoundNumber(1.6)       '2'
Debug.Print RoundNumber(-4.8)      '-5'
Debug.Print RoundNumber(101.7)     '102'
Debug.Print RoundNumber(12.535, 2) '12.54'
MarkJ