Search code examples
.netvb.netmathfilesizebitrate

Generic File Size Calculator


I'm trying to code a generic function to convert between filesizes, bytes to kb, gb to mb, etc...

The problem begins when "ToSize" value is lower than "FromSize", I don't get the correct values.

Someone can help me to fix the problems and maybe to simplify all the code?

#Region " Convert Between Sizes "

Enum FromSize As Long
    bytes = 1L
    kilobyte = 1024L
    megabyte = 1048576L
    gigabyte = 1073741824L
    terabyte = 1099511627776L
    petabyte = 1125899906842624L
End Enum

Enum ToSize As Long
    bytes = 1L
    kilobyte = 1024L
    megabyte = 1048576L
    gigabyte = 1073741824L
    terabyte = 1099511627776L
    petabyte = 1125899906842624L
End Enum

Private Function Size_To_Size(ByVal Size As Long, _
                              ByVal FromSize As FromSize, _
                              ByVal ToSize As ToSize, _
                              Optional ByVal decimals As Integer = 2 _
                              ) As Double

    Dim bytes As Double = Convert.ToDouble(Size * FromSize)
    Dim Kbs As Double = bytes * FromSize.kilobyte
    Dim mbs As Double = bytes * FromSize.megabyte
    Dim gbs As Double = bytes * FromSize.gigabyte
    Dim tbs As Double = bytes * FromSize.terabyte
    Dim pbs As Double = bytes * FromSize.petabyte

    If ToSize < FromSize Then

        Select Case ToSize
            Case ToSize.bytes : Return bytes
            Case ToSize.kilobyte : Return Kbs.ToString("n" & decimals)
            Case ToSize.megabyte : Return mbs.ToString("n" & decimals)
            Case ToSize.gigabyte : Return gbs.ToString("n" & decimals)
            Case ToSize.terabyte : Return tbs.ToString("n" & decimals)
            Case ToSize.petabyte : Return pbs.ToString("n" & decimals)
            Case Else : Return -1
        End Select

    ElseIf ToSize > FromSize Then

        Select Case ToSize
            Case ToSize.bytes : Return bytes
            Case ToSize.kilobyte : Return (Kbs / ToSize.kilobyte / 1024L).ToString("n" & decimals)
            Case ToSize.megabyte : Return (mbs / ToSize.megabyte / 1024L / 1024L).ToString("n" & decimals)
            Case ToSize.gigabyte : Return (gbs / ToSize.gigabyte / 1024L / 1024L / 1024L).ToString("n" & decimals)
            Case ToSize.terabyte : Return (tbs / ToSize.terabyte / 1024L / 1024L / 1024L / 1024L).ToString("n" & decimals)
            Case ToSize.petabyte : Return (pbs / ToSize.petabyte / 1024L / 1024L / 1024L / 1024L / 1024L).ToString("n" & decimals)
            Case Else : Return -1
        End Select

    Else ' ToSize = FromSize
        Return Size.ToString("n" & decimals)

    End If

End Function

#End Region

UPDATE:

This gives the correct result:

    MsgBox(Size_To_Size(50, FromSize.gigabyte, ToSize.bytes).ToString("n2"))
    ' Result: 53,687,091,200

This DON'T gives the correct result:

    msgbox(Size_To_Size(50, FromSize.gigabyte, ToSize.kilobyte).ToString("n2"))
    ' Result: 54.975.581.388.800,00
    ' Expected result: 52,428,800
    ' As shown here: http://www.t1shopper.com/tools/calculate/file-size/result/?size=50&unit=gigabytes

Solution

  • As an extension

    Imports System.Runtime.CompilerServices
    
    Module UnitExtension
    
        Public Enum Units
            B = 0
            KB = 1 'kilo
            MB = 2 'mega
            GB = 3 'giga
            TB = 4 'tera
            PB = 5 'peta
            EB = 6 'exa
            ZB = 7 'zetta
            YB = 8 'yotta
            'add new values as needed
            Auto = -1
        End Enum
        'compute max value of enum
        Private ReadOnly maxU As Integer = [Enum].GetValues(GetType(Units)).Cast(Of Integer)().Max
    
        'kFactor should be set according to your use
        'see
        'http://physics.nist.gov/cuu/Units/prefixes.html
        'and
        'http://physics.nist.gov/cuu/Units/binary.html
        Private ReadOnly Kfactor As Integer = 1024 'or 1000
    
        <Extension()>
        Public Function ToUnit(ByVal theNumberToConvert As Object, _
                               Optional ByVal precision As Integer = 0, _
                               Optional ByVal whichUnit As Units = Units.Auto) As String
    
            Dim _aNumber As Double = CType(theNumberToConvert, Double) 'the number being converted
            Dim _fmt As String = "n" & precision.ToString 'format string
    
            Dim _unit As Integer
            If whichUnit = Units.Auto Then 'auto unit
                _unit = CInt(Math.Floor(Math.Log(_aNumber, Kfactor))) 'yes
                If _unit > maxU Then '> max unit
                    _unit = maxU 'use max unit
                End If
            Else
                _unit = whichUnit 'no, force unit
            End If
    
            Dim _numberOfUnits As Double = _aNumber / (Kfactor ^ _unit) 'calculate number of units
    
            Return String.Format("{0} {1}", _numberOfUnits.ToString(_fmt), CType(_unit, Units))
        End Function
    
    End Module
    

    edit:

    An overload for unit to unit conversion

    <Extension()>
    Public Function ToUnit(ByVal theNumberToConvert As Object, _
                           fromUnit As Units, _
                           Optional ByVal precision As Integer = 0, _
                           Optional ByVal whichUnit As Units = Units.Auto) As String
    
        Dim _aNumber As Double = CType(theNumberToConvert, Double) * (Kfactor ^ fromUnit) 'the number being converted
        Dim _fmt As String = "n" & precision.ToString 'format string
    
        Dim _unit As Integer
        If whichUnit = Units.Auto Then 'auto unit
            _unit = CInt(Math.Floor(Math.Log(_aNumber, Kfactor))) 'yes
            If _unit > maxU Then '> max unit
                _unit = maxU 'use max unit
            End If
        Else
            _unit = whichUnit 'no, force unit
        End If
    
        Dim _numberOfUnits As Double = _aNumber / (Kfactor ^ _unit) 'calculate number of units
    
        Return String.Format("{0} {1}", _numberOfUnits.ToString(_fmt), CType(_unit, Units))
    End Function