|
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
5.3.1. Floating Point Notatie - ProblemenIn 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 2010 Broncode 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 SubEnd ModuleDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
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 2010 Broncode 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 SubEnd ModuleDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
0,2
False
84,82999
False
0,9999999 boven
5.3.2. Floating Point Notatie - RepresentatieIEEE 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 gestockeerd 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 gestockeerd.
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 2010 Broncode 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 FunctionEnd ModuleDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
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 2010 Broncode 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 FunctionEnd ModuleDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
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 zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
|