CFloat.vb is a class to handle and explain float point numbers in VB.
What are Floating-point Numbers
In computing, floating point is approximates. Floating point numbers are stored in binary format and converting from decimal number will return not the exact decimal number, but the nearest number to it that float point number can expose, as example: calculating 0.43 + 0.000001 - 0.430001, in C#, VB, VBA, Python, PHP, Java will not return 0!!
Storage Diagram
Single is stored in 4 bytes = 32 bits as the following binary storage diagram. S is signal bit, E is Exponents bit & F is fraction bit.
Fraction is called mantissa too.
Binary format of Single precision number is as follows:
Please note that the patterns may have some little differ from one compiler to other
S EEEE EEEE FFF FFFF FFFF FFFF FFFF FFFF
| Memory
Hexadecimal | Signal
Bit | Exponent
Hexadecimal | Fraction
Hexadecimal |
Epsilon
Smallest positive number
1.4E-45 | 0000 0001 | 0 | 00 | 000 0001 |
Zero; +0! | 0000 0000 | 0 | 00 | 000 0000 |
Negative Zero; -0! | 8000 0000 | 1 | 00 | 000 0000 |
1! | 3F80 0000 | 0 | 7F | 000 0000 |
The smallest number > 1
1.00000012! | 3F80 0001 | 0 | 7F | 000 0001 |
Not a number; NaN | FFC0 0000 | 1 | FF | 400 0000 |
Infinity; ∞ | 0F80 0000 | 0 | FF | 000 0000 |
Negative Infinity; -∞ | FF80 0000 | 1 | FF | 000 0000 |
Binary format of Double precision number is as given below:
S EEEE EEEE EEE FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
| Memory
Hexadecimal | Signal
Bit | Exponent
Hexadecimal | Fraction
Hexadecimal |
Epsilon
Smallest positive number
5E-324 | 0000 0000 0000 0001 | 0 | 000 | 0 0000 0000 0001 |
Zero; +0# | 0000 0000 0000 0000 | 0 | 000 | 0 0000 0000 0000 |
Negative Zero; -0# | 8000 0000 0000 0000 | 1 | 000 | 0 0000 0000 0000 |
1# | 3FF0 0000 0000 0000 | 0 | 3FF | 0 0000 0000 0000 |
The smallest number > 1
1.0000000000000002# | 3FF0 0000 0000 0001 | 0 | 3FF | 0 0000 0000 0001 |
Not a number; NaN | FFF8 0000 0000 0000 | 1 | 7FF | 8 0000 0000 0000 |
Infinity; ∞ | FF00 0000 0000 0000 | 0 | 7FF | 0 0000 0000 0000 |
Negative Infinity; -∞ | FFF0 0000 0000 0000 | 1 | 7FF | 0 0000 0000 0000 |
Unexpected Code Run Caused by Floating Point
Since floating point is approximates, then adding and mathematical operation does not always lead to the exact result.
Addition test:
public void TestAdd()
{
double V = 0;
V = 0.43 + 1E-06 - 0.430001;
if (V != 0) {
Interaction.MsgBox("It will go here!");
}
float f = 0;
f = 0.43f + 1E-06f - 0.430001f;
if (f != 0) {
Interaction.MsgBox("It will go here!");
}
}
Sub TestAdd()
Dim V As Double
V = 0.43# + 0.000001# - 0.430001#
If V <> 0 Then
MsgBox("It will go here!")
End If
Dim f As Single
f = 0.43! + 0.000001! - 0.430001!
If f <> 0 Then
MsgBox("It will go here!")
End If
End Sub
Special values:
Sub Test(x As Double)
If x > 0 Then
MsgBox("X > 0")
ElseIf x = 0 Then
MsgBox("X = 0")
ElseIf x < 0 Then
MsgBox("X < 0")
Else
MsgBox("This is a possible case! What is the value of X hear?")
Dim R =
(Double.NaN = 0) = False AndAlso
(Double.NaN < 0) = False AndAlso
(Double.NaN > 0) = False
End If
End Sub
Sub Test(x As Single)
If x > 0 Then
MsgBox("X > 0")
ElseIf x = 0 Then
MsgBox("X = 0")
ElseIf x < 0 Then
MsgBox("X < 0")
Else
MsgBox("This is a possible case! What is the value of X hear?")
Dim R =
(Single.NaN = 0) = False AndAlso
(Single.NaN < 0) = False AndAlso
(Single.NaN > 0) = False
End If
End Sub
public void Test(double x)
{
if (x > 0) {
Interaction.MsgBox("X > 0");
} else if (x == 0) {
Interaction.MsgBox("X = 0");
} else if (x < 0) {
Interaction.MsgBox("X < 0");
} else {
Interaction.MsgBox("This is a possible case! What is the value of X hear?");
dynamic R = (double.NaN == 0) == false && (double.NaN < 0) == false &&
(double.NaN > 0) == false;
}
}
public void Test(float x)
{
if (x > 0) {
Interaction.MsgBox("X > 0");
} else if (x == 0) {
Interaction.MsgBox("X = 0");
} else if (x < 0) {
Interaction.MsgBox("X < 0");
} else {
Interaction.MsgBox("This is a possible case! What is the value of X hear?");
dynamic R = (float.NaN == 0) == false &&
(float.NaN < 0) == false && (float.NaN > 0) == false;
}
}
How to Get Float Point Value from its Exponent and Fraction
The following function will get the double
value of a float point number. The Exponent Bias and Fraction Base of the float point number is dependant on the type of floating point. See the table.
Type | Total Bits | Exponent bias | Fraction base |
Half | 16 | 15 | 2^10 |
Single | 32 | 127 | 2^23 |
Double | 64 | 1023 | 2^52 |
Extended | 82 | 16383 | 2^64 |
Quad | 128 | 16383 | 2^112 |
The value of number is calculated from the formula:
Exponent <> 0:
Value = ±2^(Exponent - ExponentBias) * (1 + Fraction / FractionBase)
Exponent = 0:
Value = ±2^(1- ExponentBias) * ( Fraction / FractionBase)
Function GetDoubleValue(IsNegative As Boolean, Exponent As UInt16,
Fraction As UInt64, ExponentBias As UInt16, FractionBase As UInt64) As Double
If Exponent = 0 Then
If Fraction = 0 Then
Return If(IsNegative, -0#, 0#)
End If
Dim FractionRatio = Fraction / FractionBase
Return If(IsNegative, -1#, 1#) * (2 ^ (1 - ExponentBias)) * FractionRatio
Else
If Exponent = 2 * ExponentBias + 1 Then
If Fraction = 0 Then
Return If(IsNegative, Double.NegativeInfinity, Double.PositiveInfinity)
Else
Return Double.NaN
End If
End If
Dim FractionRatio = Fraction / FractionBase
Return If(IsNegative, -1#, 1#) * (2 ^ (CInt(Exponent) - ExponentBias)) * (1# + FractionRatio)
End If
End Function
public double GetDoubleValue(bool IsNegative, UInt16 Exponent, UInt64 Fraction,
UInt16 ExponentBias, UInt64 FractionBase)
{
if (Exponent == 0) {
if (Fraction == 0) {
return IsNegative ? -0.0 : 0.0;
}
dynamic FractionRatio = Fraction / FractionBase;
return IsNegative ? -1.0 : 1.0 * (Math.Pow(2, (1 - ExponentBias))) * FractionRatio;
} else {
if (Exponent == 2 * ExponentBias + 1) {
if (Fraction == 0) {
return IsNegative ? double.NegativeInfinity : double.PositiveInfinity;
} else {
return double.NaN;
}
}
dynamic FractionRatio = Fraction / FractionBase;
return IsNegative ? -1.0 : 1.0 *
(Math.Pow(2, (Convert.ToInt32(Exponent) - ExponentBias))) * (1 + FractionRatio);
}
}
Database primary key and float point
Since float point is approximate number; it is not a good idea to use it as primary key in a database
For example
If we have a table named Customers
with a field id
with the data type single then
Referring to a one row may result no record even if record is in the database
When we insert 0.4301 as id the real id will be different a lite and may this result unexpected result in database update or select
The flowing sql may result no record and this is depend on the database drive and how it convert numbers form decimal to float point type
Select Customer From Customers where id = 0.4301
Float Numbers and Programming Languages
References