Visual Basic 2012 Voorbeelden
   

visual basic 2012 broncode voorbeelden

Blijf op de hoogte van de recente aanpassingen op vbvoorbeelden!

Microsoft Visual Studio 2012Microsoft Developers Network - Visual BasicMicrosoft .NET Framework

5.3. Floating Point Notation - Single Double Decimal

Print Email Deel op Twitter Deel op Facebook

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.

5.3.1. Floating Point Notatie - Problemen

In onderstaand voorbeeld is het de bedoeling om van een bepaald eurobedrag ( amount) weer te geven aan de hand van welke biljetten en muntstukken ( units) men dit bedrag kan vormen.
Visual Basic 2012 Broncode - Codevoorbeeld 88
Module Example1
    Sub Main()
        Dim units As Single() = _
           {500, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01}
        '
        Dim amount As Single = 0.06
        Console.Write(amount & " : ")
        '
        Dim index As Integer
        Do While amount > 0
            Do While amount - units(index) >= 0
                Console.Write(units(index) & " ")
                amount -= units(index)
            Loop
            index += 1
        Loop
        Console.WriteLine()
        '
        Console.ReadLine()
    End Sub
End Module
Bovenstaand voorbeeld heeft echter een runtimefout IndexOutOfRangeException bij index = 15.  Men zou hier echter verwachten dat na index = 14 de restwaarde in amount 0 zou zijn, en index nooit de waarde 15 zou bereiken.  Dit is echter niet het geval, amount is op dat moment 0.009999998.
De bewerking 0.06 - 0.05 heeft als resultaat niet 0.01 maar 0.009999998 bekomen.

Bij bepaalde floating-point operaties kan men vreemde en - op het eerste zicht - onverwachte resultaten bekomen.

Deze onverwachte resultaten vloeien altijd voor uit het feit dat bepaalde decimale waarden niet exact kunnen worden gerepresenteerd in de decimal datatypes.  Deze waarden moeten benaderd worden, en zullen (bij bewerkingen) afrondingsfouten opleveren.

Neem nu de waarde 1/3, kan in het decimaal talstelsel (basis 10) via de normale representatie 0,333... nooit volledig (en dus exact) worden voorgesteld, de 3 blijft zich immers oneindig herhalen.
1/10 dan bijvoorbeeld kan in het binair talstelsel (basis 2) ook nooit volledig (en dus exact) 0.00011001100110011... worden voorgesteld, de 0011 blijft zich immers oneindig herhalen.
Welk talstelsel men ook gebruik, er zullen altijd waarden zijn, die niet volledig (en dus exact) kunnen worden voorgesteld.  Zeker irrationele getallen (getallen die niet kunnen worden voorgesteld aan de hand van een breuk), zoals vele vierkantswortels of de wiskunde constanten pi en e, zullen problemen geven.

Men zou alle rationele getallen exact kunnen representeren door zowel de deler als het deeltal te bewaren (of dus als breuk (van gehele getallen) voor te stellen), maar dit is dus geen oplossing voor de irrationele getallen.

Geen enkel schema met een eindige capaciteit kan alle mogelijke decimale waarden exact voorstellen.  Men kan immers nooit een oneindige reeks van mogelijke (reële) waarden voorstellen in een eindig aantal bits.

De meeste omgevingen (zoals ook .NET) gebruiken de floating-point notatie om decimale waarden voor te stellen.  Geen perfect systeem, maar door te voldoen aan de standaard IEEE 754 voor floating-point notaties, garandeert .NET op zijn minst dat gestandaardiseerde (lees : gekende) technieken worden gebruikt, om met benadering/afrondingen om te gaan.

Hieronder zie je een ander voorbeeld waarbij - op het eerst zicht - onverwachte resultaten optreden.
Visual Basic 2012 Broncode - Codevoorbeeld 89
Module Example2
    Sub Main()
        Console.WriteLine(2.0 Mod 0.2 = 0)
        Console.WriteLine(2.0 Mod 0.2)
        '
        Dim someSingle As Single = 4.99
        Console.WriteLine(someSingle * 17 = 84.83)
        Console.WriteLine(someSingle * 17)
        '
        someSingle = 1 / 107.0
        Console.WriteLine(someSingle * 107 = 1)
        Console.WriteLine(someSingle * 107)
        '
        Console.ReadLine()
    End Sub
End Module
Console Application Output
False
0,2
False
84,82999
False
0,9999999

5.3.2. Floating Point Notatie - Representatie

IEEE 754 Single Precision (zoals Single in .NET) :

1 bit voor sign (s) + 8 bits voor exponent (e) + 23 bits voor mantisse (m) = 32 bits

Enkele afspraken over onderstaande notaties :
- binaire waarden staan steeds tussen vierkante haakjes ([ ]) weergegeven
- het symbool ~ betekent hier ongeveer

Binary format :
seee eeee emmm mmmm mmmm mmmm mmmm mmmm
Er zijn verschillende representaties van floating-point getallen/waarden.

Hieronder worden ze opgesomd in de volgorde waarin ze behandeld worden :

- normalised
- zero (negative en positive zero)
- subnormal (denormalised)
- infinity (positive en negative infinity)
- not-a-number (NaN)

Normalised Representation :

Deze representatie is van toepassing voor de meeste getallen.

General formula :
(-1)^[s] * [1.mmmm mmmm mmmm mmmm mmm] * 2^[eeee eeee]
Sign :
[0] (-1)^0 =  1
or
[1] (-1)^1 = -1
Exponent :

Dit is bewaard als een unsigned byte waarde, maar om ook heel kleine waarden te kunnen voorstellen (met een negatieve exponent), wordt er een offset gebruikt van -127 (deze wordt ook wel de bias genoemd).

Enkele mogelijke exponenten :
[0000 0000] = 0                -> reserved for other representations
[0000 0001] = 1   - 127 = -126 -> minimum exponent
...
[0111 1110] = 126 - 127 =   -1
[0111 1111] = 127 - 127 =    0
[1000 0000] = 128 - 127 =    1
...
[1111 1110] = 254 - 127 =  127 -> maximum exponent
[1111 1111] = 255              -> reserved for other representations
De exponenten 0 en 255 worden gereserveerd voor andere respresentaties,
verderop hierover meer.

Mantisse :

Het getal 0,5 zou men kennen voorstellen als 1 * 2^-1 of als 0.5 * 2^0 of als 0.25 * 2^1 of als 0.125 * 2^2 of als ... .  Men kan dus steeds de mantisse met 2 delen en de exponent met 1 verhogen om hetzelfde resultaat te bekomen.  Met andere woorden, een getal zou meerdere representaties kennen.  Hierdoor gaat natuurlijk plaats (lees : formaat representaties) verloren om andere getallen voor te stellen.

Daarom wordt vastgelegd in de genormaliseerde representatie om de significant zo groot mogelijk te maken (en dus geen significante bits te verliezen, wanneer men met [0]n zou beginnen) en de exponent zo klein mogelijk te houden.  Dit proces ook wel wordt normalisatie genoemd.

De significant wordt in deze representatie voorafgegaan door [1.].  In principe bestaat hier de mantisse dus uit 24 cijfers, maar omdat bij deze representatie de mantisse steeds begint met [1] ([1.]) hoeft deze niet te worden bewaard.

De minimum waarde voor de mantisse is :
[1.0000 0000 0000 0000 0000 000] or 1
De maximum waarde voor de mantisse is :
[1.1111 1111 1111 1111 1111 111] or 1,999999940395355224609375 or 2^1 - 2^-24
Over het algemeen kan je stellen :
1 <= mantissa < 2
De kleinste genormaliseerde waarde heeft mantisse 1 en exponent -126 :
1 * 2^-126 or ~ 1,1754E-38 .
De grootste genormaliseerde waarde heeft mantisse 1,999999940395355224609375
en exponent 127 :
1,999999940395355224609375 * 2^127 or ~ 3.4028E+38
Enkele mogelijke representaties van genormaliseerde waarden :
[0000 0000 1000 0000 0000 0000 0000 0000] or  ~ 1,1754E-38
-> minimum positive value
...
[0011 1111 0000 0000 0000 0000 0000 0000] or    0,5
...
[0011 1111 0001 1001 1001 1001 1001 1010] or    0,6
...
[0111 1111 0111 1111 1111 1111 1111 1111] or ~  3,4028E+38
-> maximum positive value -> 'Single.MaxValue'
...
[1000 0000 1000 0000 0000 0000 0000 0000] or ~ -1,1754E-38
-> minimum negative value
...
[1111 1111 0111 1111 1111 1111 1111 1111] or ~ -3,4028E+38
-> maximum negative value -> 'Single.MinValue'
0,5 zal dus worden gerepresenteerd met sign 1 (of [0]), mantisse 1 of [1.0000 0000 0000 0000 0000 000]) en exponent -1 (of [0111 1110]), of 1 * 1 * 2^-1.

0,6 zal dus worden gerepresenteerd met sign 1 (of [0]), mantisse 1,1935484 of [1.0011 0011 0011 0011 0011 010]) en exponent -1 (of [0111 1110]), of 1 * 1,1935484 * 2^-1.

Representation of Zero :

Hoe wordt 0 dan gerepresenteerd?  Noch de exponent, noch de mantisse kan 0 zijn, dus het product van beide kan nooit 0 geven.

Voor 0 is een specifieke representatie voorzien, of zelf 2 representaties ( 1 voor +0 en 1 voor -0) voorzien.

Zowel de significant als de exponent zijn 0 voor de representatie van 0.
[0000 0000 0000 0000 0000 0000 0000 0000] -> +0
[1000 0000 0000 0000 0000 0000 0000 0000] -> -0
Subnormal (Denormalized) Representation :

General formula :
(-1)^[s] * [0.mmmm mmmm mmmm mmmm mmm] * 2^-126
Deze representatie wordt gebruik om hele kleine getallen voor te stellen.

Exponent :

De exponent is hier (net zoals bij de representatie voor 0) steeds [0000 0000] of 0.  Deze 0 heeft echter geen betekenis, de exponent is hier steeds -126 (dezelfde als de minimum exponent in de genormaliseerde representatie).

Mantisse :

De mantisse is niet genormaliseerd.  En verondersteld een voorafgaande [0.].

De kleinste mantisse is :
[0.0000 0000 0000 0000 0000 001] or 0,00000011920928955078125  or 2^-23
De grootste mantisse is :
[0.1111 1111 1111 1111 1111 111] or 0,999999940395355224609375 or 2^0 - 2^-24
De kleinste voor te stellen gedenormaliseerde waarde is :
0,00000011920928955078125  * 2^-126 or ~ 1,4012E-45
De grootste voor te stellen gedenormaliseerde waarde is :
0,999999940395355224609375 * 2^-126 or ~ 1,1754E-38
[0000 0000 0000 0000 0000 0000 0000 0001] or ~  1,4012E-45
-> minimum positive value -> 'Single.Epsilon'
...
[0000 0000 0111 1111 1111 1111 1111 1111] or ~  1,1754E-38
-> maximum positive value
...
[1000 0000 0000 0000 0000 0000 0000 0001] or ~ -1,4012E-45
-> minimum negative value
...
[1000 0000 0111 1111 1111 1111 1111 1111] or ~ -1,1754E-38
-> maximum negative value
Representation of Infinities :

Exponent :

De exponent is hier steeds [1111 1111] of 255.  Deze 255 heeft hier echter geen betekenis, maar maakt deel uit van de formaatrepresentatie voor oneindig.

Mantisse :

De mantisse is hier steeds [000 0000 0000 0000 0000 0000] of 0.  Deze 0 heeft hier echter geen betekenis, maar maakt deel uit van de formaatrepresentatie voor oneindig.

Sign :

De sign bit geeft aan of het hier gaat om positieve of negatieve oneindigheid.
[0111 1111 1000 0000 0000 0000 0000 0000] -> 'Single.PositiveInfinity'
[1111 1111 1000 0000 0000 0000 0000 0000] -> 'Single.NegativeInfinity'
Visual Basic 2012 Broncode - Codevoorbeeld 90
Module Example3
    Public Sub Main()
        Console.WriteLine("seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm")
        Console.WriteLine(GetBinary(1.17549435E-38F) & " : " & _
                          1.17549435E-38F.ToString())
        Console.WriteLine(GetBinary(0.5F) & " : " & 0.5F.ToString())
        Console.WriteLine(GetBinary(0.6F) & " : " & 0.6F.ToString())
        Console.WriteLine(GetBinary(Single.MaxValue) & " : " & _
                          Single.MaxValue.ToString())
        Console.WriteLine(GetBinary(-1.17549435E-38F) & " : " & _
                          -1.17549435E-38F.ToString())
        Console.WriteLine(GetBinary(Single.MinValue) & " : " & _
                          Single.MinValue.ToString())
        Console.WriteLine(GetBinary(0.0F) & " : " & 0.0F.ToString())
        Console.WriteLine(GetBinary(-0.0F) & " : " & -0.0F.ToString())
        Console.WriteLine(GetBinary(Single.Epsilon) & " : " & _
                          Single.Epsilon.ToString())
        Console.WriteLine(GetBinary(-1.401298E-45F) & " : " & _
                          -1.401298E-45F.ToString())
        Console.WriteLine(GetBinary(Single.PositiveInfinity) & " : " & _
                          Single.PositiveInfinity.ToString())
        Console.WriteLine(GetBinary(Single.NegativeInfinity) & " : " & _
                          Single.NegativeInfinity.ToString())
        '
        Console.ReadLine()
    End Sub
    Public Function GetBinary(ByVal value As Byte) As String
        For counter As Integer = 1 To 8
            GetBinary = (value Mod 2).ToString() & GetBinary
            value >>= 1
        Next
    End Function
    Public Function GetBinary(ByVal value As Single) As String
        If BitConverter.IsLittleEndian Then
            For Each byteElement As Byte In BitConverter.GetBytes(value)
                GetBinary = GetBinary(byteElement) & GetBinary
            Next
        Else
            Throw New ApplicationException("Only Little Endian supported.")
        End If
    End Function
End Module
Console Application Output
seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
00000000100000000000000000000000 : 1,175494E-38
00111111000000000000000000000000 : 0,5
00111111000110011001100110011010 : 0,6
01111111011111111111111111111111 : 3,402823E+38
10000000100000000000000000000000 : -1,175494E-38
11111111011111111111111111111111 : -3,402823E+38
00000000000000000000000000000000 : 0
10000000000000000000000000000000 : 0
00000000000000000000000000000001 : 1,401298E-45
10000000000000000000000000000001 : -1,401298E-45
01111111100000000000000000000000 : oneindig
11111111100000000000000000000000 : -oneindig
Operations on Zero, NaN and Infinity :

Operaties op speciale waarden (zero, NaN en infinity) zullen volgens de IEEE 754 standaard een specifiek resultaat opleveren.

Elke operatie die gebruik maakt van een NaN operand, zal resulteren in een NaN resultaat.

Enkele andere operaties gaan als volgt :
Visual Basic 2012 Broncode - Codevoorbeeld 91
Module Example4
    Sub Main()
        Dim singleOperands As Single() = {Single.PositiveInfinity, _
                                          Single.NegativeInfinity, _
                                          123.0F, -123.0F, 0.0F, -0.0F}
        Dim operatorSymbols As String() = {"*", "/", "+", "-"}
        '
        For Each operatorSymbol As String In operatorSymbols
            Console.WriteLine("OPERATOR " & operatorSymbol.ToString())
            Console.WriteLine()
            For Each singleOperand1 As Single In singleOperands
                For Each singleOperand2 As Single In singleOperands
                    PrintCalculation(singleOperand1, operatorSymbol, _
                                     singleOperand2)
                Next
                Console.WriteLine()
            Next
            Console.WriteLine()
        Next
        '
        Console.ReadLine()
    End Sub
    Sub PrintCalculation(ByVal operand1 As Single, _
                         ByVal operatorSymbol As String, _
                         ByVal operand2 As Single)
        Console.Write(GetString(operand1) & " " & operatorSymbol & " " & _
                      GetString(operand2) & " = ")
        Select Case operatorSymbol
            Case "*"
                Console.WriteLine(GetString(operand1 * operand2))
            Case "/"
                Console.WriteLine(GetString(operand1 / operand2))
            Case "+"
                Console.WriteLine(GetString(operand1 + operand2))
            Case "-"
                Console.WriteLine(GetString(operand1 - operand2))
        End Select
    End Sub
    Function GetString(ByVal value As Single) As String
        If IsPositiveZero(value) Then
            GetString = "+0"
        ElseIf IsNegativeZero(value) Then
            GetString = "-0"
        ElseIf Single.IsNegativeInfinity(value) Then
            GetString = "-Infinity"
        ElseIf Single.IsPositiveInfinity(value) Then
            GetString = "+Infinity"
        ElseIf Single.IsNaN(value) Then
            GetString = "NaN"
        Else
            GetString = value.ToString()
        End If
    End Function
    Public Function IsPositiveZero(ByVal value As Single) As Boolean
        If BitConverter.GetBytes(value)(0) = 0 AndAlso _
           BitConverter.GetBytes(value)(1) = 0 AndAlso _
           BitConverter.GetBytes(value)(2) = 0 AndAlso _
           BitConverter.GetBytes(value)(3) = 0 Then _
           IsPositiveZero = True
    End Function
    Public Function IsNegativeZero(ByVal value As Single) As Boolean
        If BitConverter.GetBytes(value)(0) = 0 AndAlso _
           BitConverter.GetBytes(value)(1) = 0 AndAlso _
           BitConverter.GetBytes(value)(2) = 0 AndAlso _
           BitConverter.GetBytes(value)(3) = 128 Then _
           IsNegativeZero = True
    End Function
End Module
Console Application Output
OPERATOR *

+Infinity * +Infinity = +Infinity
+Infinity * -Infinity = -Infinity
+Infinity * 123 = +Infinity
+Infinity * -123 = -Infinity
+Infinity * +0 = NaN
+Infinity * -0 = NaN

-Infinity * +Infinity = -Infinity
-Infinity * -Infinity = +Infinity
-Infinity * 123 = -Infinity
-Infinity * -123 = +Infinity
-Infinity * +0 = NaN
-Infinity * -0 = NaN

123 * +Infinity = +Infinity
123 * -Infinity = -Infinity
123 * 123 = 15129
123 * -123 = -15129
123 * +0 = +0
123 * -0 = -0

-123 * +Infinity = -Infinity
-123 * -Infinity = +Infinity
-123 * 123 = -15129
-123 * -123 = 15129
-123 * +0 = -0
-123 * -0 = +0

+0 * +Infinity = NaN
+0 * -Infinity = NaN
+0 * 123 = +0
+0 * -123 = -0
+0 * +0 = +0
+0 * -0 = -0

-0 * +Infinity = NaN
-0 * -Infinity = NaN
-0 * 123 = -0
-0 * -123 = +0
-0 * +0 = -0
-0 * -0 = +0


OPERATOR /

+Infinity / +Infinity = NaN
+Infinity / -Infinity = NaN
+Infinity / 123 = +Infinity
+Infinity / -123 = -Infinity
+Infinity / +0 = +Infinity
+Infinity / -0 = -Infinity

-Infinity / +Infinity = NaN
-Infinity / -Infinity = NaN
-Infinity / 123 = -Infinity
-Infinity / -123 = +Infinity
-Infinity / +0 = -Infinity
-Infinity / -0 = +Infinity

123 / +Infinity = +0
123 / -Infinity = -0
123 / 123 = 1
123 / -123 = -1
123 / +0 = +Infinity
123 / -0 = -Infinity

-123 / +Infinity = -0
-123 / -Infinity = +0
-123 / 123 = -1
-123 / -123 = 1
-123 / +0 = -Infinity
-123 / -0 = +Infinity

+0 / +Infinity = +0
+0 / -Infinity = -0
+0 / 123 = +0
+0 / -123 = -0
+0 / +0 = NaN
+0 / -0 = NaN

-0 / +Infinity = -0
-0 / -Infinity = +0
-0 / 123 = -0
-0 / -123 = +0
-0 / +0 = NaN
-0 / -0 = NaN


OPERATOR +

+Infinity + +Infinity = +Infinity
+Infinity + -Infinity = NaN
+Infinity + 123 = +Infinity
+Infinity + -123 = +Infinity
+Infinity + +0 = +Infinity
+Infinity + -0 = +Infinity

-Infinity + +Infinity = NaN
-Infinity + -Infinity = -Infinity
-Infinity + 123 = -Infinity
-Infinity + -123 = -Infinity
-Infinity + +0 = -Infinity
-Infinity + -0 = -Infinity

123 + +Infinity = +Infinity
123 + -Infinity = -Infinity
123 + 123 = 246
123 + -123 = +0
123 + +0 = 123
123 + -0 = 123

-123 + +Infinity = +Infinity
-123 + -Infinity = -Infinity
-123 + 123 = +0
-123 + -123 = -246
-123 + +0 = -123
-123 + -0 = -123

+0 + +Infinity = +Infinity
+0 + -Infinity = -Infinity
+0 + 123 = 123
+0 + -123 = -123
+0 + +0 = +0
+0 + -0 = +0

-0 + +Infinity = +Infinity
-0 + -Infinity = -Infinity
-0 + 123 = 123
-0 + -123 = -123
-0 + +0 = +0
-0 + -0 = -0


OPERATOR -

+Infinity - +Infinity = NaN
+Infinity - -Infinity = +Infinity
+Infinity - 123 = +Infinity
+Infinity - -123 = +Infinity
+Infinity - +0 = +Infinity
+Infinity - -0 = +Infinity

-Infinity - +Infinity = -Infinity
-Infinity - -Infinity = NaN
-Infinity - 123 = -Infinity
-Infinity - -123 = -Infinity
-Infinity - +0 = -Infinity
-Infinity - -0 = -Infinity

123 - +Infinity = -Infinity
123 - -Infinity = +Infinity
123 - 123 = +0
123 - -123 = 246
123 - +0 = 123
123 - -0 = 123

-123 - +Infinity = -Infinity
-123 - -Infinity = +Infinity
-123 - 123 = -246
-123 - -123 = +0
-123 - +0 = -123
-123 - -0 = -123

+0 - +Infinity = -Infinity
+0 - -Infinity = +Infinity
+0 - 123 = -123
+0 - -123 = 123
+0 - +0 = +0
+0 - -0 = +0

-0 - +Infinity = -Infinity
-0 - -Infinity = +Infinity
-0 - 123 = -123
-0 - -123 = 123
-0 - +0 = -0
-0 - -0 = +0

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.