Data types in VB and .NET

This article presents a technical look into the numeric data types supported by classic Visual Basic, VBA, VB.NET and the .NET framework. There are surprising peculiarities in several types. A conversion diagram shows how to achieve lossless type conversion.

How to store numbers in your programs? How to avoid Overflow or loss of decimals? How to work with dates? This article summarizes the elementary numeric data types in classic Visual Basic, Visual Basic for Applications, Visual Basic .NET and other .NET languages.

Reasonable development experience is assumed. This is no introduction to programming. You may find some of the information especially handy if you intend to migrate from VB6 to a .NET language.

Data type summary

With numeric data types we mean the elementary built-in types intended to store integer numbers, decimal numbers, flags and dates.

The following table shows numeric data types sorted by their storage requirements in bits and the numeric range they support. The VB6/VBA data types are used in classic VB and Visual Basic for Applications. The .NET data types are offered by the .NET framework versions from 2.0 to 4.0. The VB.NET data types are aliases for the underlying .NET data types, available in Visual Basic versions from 2005 to 2010.

Numeric data types in VB and .NET
Bits VB6/VBA .NET VB.NET Min value Max value
(1) Boolean Boolean -1 0
8 SByte -128 127
8 Byte Byte 0 255
16 Integer % Int16 Short -32768 32767
16 UInt16 UShort 0 65535
32 Long & Int32 Integer % -2,147,483,648 2,147,483,647
32 UInt32 UInteger 0 4,294,967,295
64 Currency @ -922,337,203,685,477.5808 922,337,203,685,477.5807
64 VBA7: LongLong ^ Int64 Long & -9,223,372,036,854,775,808 9,223,372,036,854,775,807
64 UInt64 ULong 0 18,446,744,073,709,551,615
128 Decimal Decimal @ -7.92E+28 * 7.92E+28 *
32 Single ! Single ! -3.4028235E+38 3.4028235E+38
64 Double # Double # -1.80E+308 ** 1.80E+308 **
Date and time
64 Date -657434.999994212936159 2958465.999999999767
64 DateTime Date -9,223,372,036,854,775,808 7,767,064,994,427,387,903
64 TimeSpan -9,223,372,036,854,775,808 9,223,372,036,854,775,807

* Decimal: ±79,228,162,514,264,337,593,543,950,335
** Double: ±1.79769313486231570E+308

Data type specifics

Boolean

Boolean is really a flag and not a numeric data type. The storage size is 16 bits in VB6 and varies by platform in .NET.

Integer and Long

Integer and Long have the same name and type character in VB6/VBA and VB.NET, but they are different data types altogether. To stay clear one can use Int16, Int32 and Int64 in .NET.

Currency

Currency is a 64-bit integer divided by 10000. It is only available in VB6/VBA. In VB.NET one can replace it with Decimal, which provides more capability.

LongLong

Introduced with Office 2010, LongLong is a 64-bit signed integer that is available in VBA7 running on a 64-bit platform. It is unsupported in VB6, VBA6 and on 32-bit platforms.

Decimal

Decimal is a signed 96-bit integer, which is divided by a scaling factor. The scaling factor varies from 1 to 1E+28. A Decimal can represent 29 decimal digits: integers and decimal numbers with max 28 decimal places. The binary representation consists of a 1-bit sign, a 96-bit integer number and the scaling factor. The smallest nonzero Decimal is ±1E−28 = ±0.0000000000000000000000000001. The storage size is 128 bits, but not all the bits are used for the number.

VB6 supports Decimal with the Variant data type. You cannot declare a variable to be of type Decimal. You can, however, create a Variant whose subtype is Decimal using the CDec function.

Single

The value closest to zero is ±1.401298E-45.

VB6: The maximum Single is 3.4028235E+38. It rounds down to 3.402823E+38 when displayed. The latter is also the maximum Single literal you can write in VB6 code.

.NET: NaN, PositiveInfinity and NegativeInfinity are special values of Single.

Double

The value closest to zero is ±4.94065645841247E-324 in VB6 and ±4.94065645841246544E-324 in .NET.

VB6 stores max 1.7976931348623157E308. It rounds down to 1.79769313486231E+308 when displayed. The latter value is also the maximum literal number you can write in VB6 code. The absolute maximum is 1.7976931348623158077E308, which equals 1.7976931348623157E308 due to rounding.

.NET: NaN, PositiveInfinity and NegativeInfinity are special values of Double.

Date and time values

Dates and times are closely related to numeric data types, especially in VB6.

VB6 date and time

VB6 stores dates and times in the Date data type. Date can store either the calendar date, the time of day, or both. The range covered is midnight #1/1/0100# to #12/31/9999 11:59:59 PM# in the Gregorian calendar. The underlying data type is Double. Use CDbl(T) to convert a Date to its numeric representation and CDate(n) to convert a number to a Date.

The integral part of a Date represents the day. The value zero is equal to #12/30/1899#. This is the Epoch. The value 1 is equal to one day, or 24 hours. A week equals 7 and a year is either 365 or 366. Sample dates: #1/1/1900# = 2, #1/1/2000# = 36526, #1/1/2100# = 73051.

The time of day is in the decimal fraction. An hour is equal to 1/24, a minute is 1/1440 and a second is 1/86400. Noon is 0.5. Noon on #1/1/1900# equals 2.5 and so on. The maximum precision in VB6 time functions is 1 second, whose numeric value is 0.0000115740740740741. You can even store fractions of a second, but VB6 has no functionality for the fractions.

Peculiarities with historical dates in VB6

The integral part is the date, the fraction is the time: date.time. That's easy. Things get odd when the value is negative. This is on or before #12/30/1899#.

With modern dates time always runs forwards, as you would suspect. With negative historical dates time actually runs backwards! Midnight #1/1/1800# equals −36522, but noon #1/1/1800# is −36522.5 (less than midnight!) and one second before midnight is −36522.9999884259 (even less). At midnight the clock jumps forward to −36521, which equals #1/2/1800#. The decimal fraction still shows the time and the integral part is the date, but each second decrements the clock while each new day increments it, not just by 1, but by almost 2. Negative times are really counterintuitive.

To make things worse, time values for #12/30/1899# are ambigous in two ways. First, a time value without a date equals that time on #12/30/1899#. This means that 0.5 is either noon or noon on #12/30/1899#, depending on context. Zero is either midnight or #12/30/1899# or midnight on #12/30/1899#. The other ambiguity is that all time values come in double for #12/30/1899#. 0.5 is noon #12/30/1899#, but -0.5 is noon #12/30/1899# as well. The integral part is the date, the fraction is the time.

Another surprise is here: #12/30/1899 11:59:59 PM# - #12/29/1899 11:59:59 PM# = 2.99997685185185. Not 1, what you normally would expect for a 24-hour period. Be careful when working with historical dates.

Expressions for VB6 date/time

Good expressions:
Get date without time: Fix(T)
Get time without date: Abs(T-Fix(T))
Get time difference in days: DateDiff("s", T1, T2)/86400

Avoid these:
Get date without time: Int(T) – Wrong for historical dates
Get time without date: T-Int(T) – Wrong for historical dates
Get time difference in days: T2-T1 – Wrong for historical dates with time

Bug with DateDiff: DateDiff("s", -0.5, 0.5) = 1 second, even though it should be 0 seconds.

.NET date and time

DateTime represents dates and times with a 64-bit value, which includes 62 bits for the date/time and 2 bits for time zone information (the Kind property). The range covered is midnight #1/1/0001# to #12/31/9999 11:59:59.9999999 PM# in the Gregorian calendar. Date/time is stored as "ticks". Each tick equals 100 nanoseconds. The supported range of ticks is from 0 to 3,155,378,975,999,999,999. The "raw" numeric range of the data type is larger because of the time zone field. The value zero is equal to #1/1/0001#. The smallest non-zero time is 1 tick = 100ns.

TimeSpan represents a time interval (duration), either positive or negative. It can also represent the time of day unrelated to a particular date. TimeSpan is a 64-bit type whose underlying numeric range is equal to that of Int64. Time is stored as ticks of 100 nanoseconds each. The range covered is −10675199.02:48:05.4775808 to 10675199.02:48:05.4775807 as measured in days, hours, minutes, seconds and fractions of a second. This is approximately ±29228 years, which means a TimeSpan can go over the date range supported by DateTime.

DateTimeOffset is a structure representing a DateTime value along with its time difference to UTC. This structure is outside the scope of this article.

Data type conversion diagram

The following diagram shows the possible data type conversions in VB6 and .NET.
Numeric data type conversions pdf

There are two considerations when converting a value from a data type to another: magnitude and precision.

  1. Magnitude: An error may occur if the converted value is too large (or too small) to fit in the target data type.
  2. Precision: Loss of precision may occur when converting to a Single or Double. In this case, magnitude is preserved, but some less significant digits may be lost. Loss of precision does not cause a run-time error but it may lead to erroneous results.

Data types in VB and .NET
URN:NBN:fi-fe201012093072