Can anyone offer any advice on how to get the mantissa and exponent from a double in VB.net? I know I can do a string parse and some conversion to ints but I wondered if anyone had a mathematical equivalent formula that would allow me to do this?
Many thanks
Do you want to get the "native" mantissa and exponent within the IEEE-754 value? That's actually fairly easy: use BitConverter.DoubleToInt64Bits to get the value as an integer (which is easier to perform bit operations on) and then see my article on .NET binary floating point types for which bits are where.
I have some C# code which extracts the various parts in order to convert it to a precise decimal representation - you could convert the relevant bits of that into VB reasonably easily.
You should try this:
Public Function DeclString(ByVal dDegrees As Double) As String
Dim Flag As String
Dim ddecimal As Double
Dim iDegrees As Integer
If dDegrees < 0 Then Flag = "S" Else Flag = "N"
iDegrees = Int(Abs(dDegrees))
ddecimal = (Abs(dDegrees) - iDegrees) * 60 ' + 0.5
If ddecimal > 59.5 Then iDegrees = iDegrees + 1: ddecimal = 0
DeclString = Format$(iDegrees, "00") + Flag + Format$(ddecimal, "00")
End Function
Related
Vb.net has a decimal data type.
Unlike normal double or floating points, decimal data type can store values like 0.1
Now say I have a variable like precision.
Say precision is 8
So basically I want to do
Protected Overridable Sub setPairsPricesStep2(decimalPrecission As Long, Optional base As String = "", Optional quote As String = "")
If decimalPrecission = 8 Then
Return
End If
Dim price = 10D ^ (-decimalPrecission)
setPairsPriceStep1(price, base, quote)
End Sub
There is a problem there
the result of Dim price = 10D ^ (-decimalPrecission) is double, not decimal. I can convert it to decimal but then I will lost the precission.
So what is the right way to do it? Should I just use for next but that's hardly elegant.
It's simple
I want a function that given precisions give decimal value.
For example, if precision is 1 I got 0.1. If precision is 5, I got 0.00001
I ended up doing this
For i = 1 To decimalPrecission
price *= 0.1D
Next
But surely there is a better way
Update:
Per comment, I tried
Dim e = 10D ^ -5
Dim e1 = 10D ^ -5L
The type of e and e1 are both double.
I suppose I can do Cdec(e). But then it means I have lost accuracy because normal double cannot store .1 correctly.
I want a function that given precisions give decimal value.
For example, if precision is 1 I got 0.1. If precision is 5, I got 0.00001
Since you are working with the Decimal type, the simplest way to get this result is to use the Decimal constructor that allows you to specify the scale factor.
Public Sub New (lo As Integer, mid As Integer, hi As Integer, isNegative As Boolean, scale As Byte)
From the Remarks section of the above referenced documentation,
The binary representation of a Decimal number consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the integer number and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10 raised to an exponent ranging from 0 to 28.
So you can see that if take the value of one divided by 10 to the first power, the result is 0.1. Likewise, one divided by 10 to the fifth power, the result is 0.00001.
The lo, mid, and hi arguments in the constructor could be obtained by uisng the [Decimal.GetBits Method](Decimal.GetBits Method), but for this simple case, I chose to hard code the values for the value of one stored as a decimal.
To obtain a value of 0.1D:
New Decimal(1, 0, 0, False, 1)
To obtain a value of 0.00001D:
New Decimal(1, 0, 0, False, 5)
Dim stringrepresentation = "1E-" + decimalPrecission.ToString
Dim price = Decimal.Parse(stringrepresentation, System.Globalization.NumberStyles.AllowExponent)
This is what I basically did. Basically I created a string 1E-5, for example, and use decimal.parse to get the decimal
I wonder if there is a better way but I have no idea.
Actually Jimy ways may work too but rounded to a number
I have three numbers from my database and want to compare them in an if statement.
I have a simple conevert function that returns only doubles.
Public Function RetDbl(ByVal obj As Variant) As Double
On Error Resume Next
RetDbl = val(Replace(Nz(obj, 0), ",", "."))
End Function
The statement is
If RetDbl(rs.value("NumA")) + RetDbl(rs.value("NumB")) <> (RetDbl(rs.value("NumC")) * 1000) Then
'[... do some code ...]
End If
With RetDbl(rs.value("NumA")) = 0.33, RetDbl(rs.value("NumB") = 0.5 and RetDbl(rs.value("NumC")) = 0.00083
This always returns false
I also tried:
In the direct field (STRG + G): ?cdbl(0.33) + cdbl(0.50) = cdbl(0.83) returns false. When i leave out the last part it returns 0.83
How can i compare these numbers?
Comparing floating numbers is hard. Only yesterday, I've posted this question
My solution:
Public Function DblSafeCompare(ByVal Value1 As Variant, ByVal Value2 As Variant) As Boolean
'Compares two variants, dates and floats are compared at high accuracy
Const AccuracyLevel As Double = 0.00000001
'We accept an error of 0.000001% of the value
Const AccuracyLevelSingle As Single = 0.0001
'We accept an error of 0.0001 on singles
If VarType(Value1) <> VarType(Value2) Then Exit Function
Select Case VarType(Value1)
Case vbSingle
DblSafeCompare = Abs(Value1 - Value2) <= (AccuracyLevelSingle * Abs(Value1))
Case vbDouble
DblSafeCompare = Abs(Value1 - Value2) <= (AccuracyLevel * Abs(Value1))
Case vbDate
DblSafeCompare = Abs(CDbl(Value1) - CDbl(Value2)) <= (AccuracyLevel * Abs(CDbl(Value1)))
Case vbNull
DblSafeCompare = True
Case Else
DblSafeCompare = Value1 = Value2
End Select
End Function
Note that the AccuracyLevel (epsilon) could be set to a smaller value, and I'm using the same value for singles and doubles, but it did well for my purposes.
I'm using a relative epsilon, but multiplying it with the first, and not the largest value, since if there's a significant difference the comparison will fail anyway.
Note that I'm using <= and not < since else DblSafeCompare(cdbl(0) ,cdbl(0)) would fail
Note that this function checks for type equality, so comparing integers to longs, doubles to singles, etc. all fails. Comparing Null to Null passes, however.
Implement it:
?DblSafeCompare(cdbl(0.33) + cdbl(0.50) ,cdbl(0.83))
?DblSafeCompare(cdbl(0.331) + cdbl(0.50) ,cdbl(0.83))
Comparing floating point numbers is really an issue, if you try to do it without understanding of the nature of the floating numbers.
Here is a nice article about it - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html and How dangerous is it to compare floating point values?
In general, this problem is so big, that some languages like C# have developed a specific class called Decimal which makes comparing run as it would be expected by a non-programmer. Decimal info. In VBA, a similar class is Currency. Thus
CCur(0.33) + CCur(0.50) = CCur(0.83)
Returns True. VBA supports the function CDec, which converts a double to a decimal number, but it does not support the class Decimal. Thus:
CDec(0.33) + CDec(0.50) = CDec(0.83)
would also return True. And is with some better accuracy than Currency. CDec documentation.
I don't know why it does this but here is the code linked to it not show in the picture.
In the My.Settings the type is string
Dim cursorValue as BigInteger
Dim cursorPrice as BigInteger
Dim xCursor as Decimal
My.Settings.cursorValue = cursorValue.ToString
My.Settings.cursorPrice = cursorPrice.ToString
My.Settings.xCursor = xCursor
Error:
To Update Values Displayed:
cursorUpgrade.Text = "Cursors: " & cursorValue.ToString("N0") & Environment.NewLine & "Upgrade: " & cursorPrice.ToString("N0")
You can't mix BigInteger and decimal values in arithmetic directly in VB.
One way to handle this would be to scale your decimal value up by a power of 10 and make it a BigInteger and perform your aritmetic in the BigInteger domain. So, if you need to multiply your BigInteger by an arbitrary decimal number, and you know what precision you're willing to accept, you could do (assuming you want to maintain 4 decimal places of precision when multiply your decimal number):
cursorValue *= (BigInteger)(xCursor * 10000)
cursorValue /= 10000
In the case of adding 1, just add 1 instead of attempting to add xCursor:
cursorValue += 1
Or, if you know you want to add xCursor as an integer:
cursorValue += (BigInteger)xCursor
The problem you are having seems to to in the code in your linked picture
cursorValue += xCursor
Won't work if cursorValue is BigInteger and xCursor is Dewcimal for the reasons given in the error message. Instead you could use
cursorValue += New BigInteger(xCursor)
Problems like this drive me crazy. Here's the relevant piece of code:
Dim RES As New Size(Math.Floor(Math.Round(mPageSize.Width - mMargins.Left - mMargins.Right - mLabelSize.Width, 4) / (mLabelSize.Width + mSpacing.Width) + 1),
Math.Floor((mPageSize.Height - mMargins.Top - mMargins.Bottom - mLabelSize.Height) / (mLabelSize.Height + mSpacing.Height)) + 1)
Values of the variables (all are of Single type):
mPageSize.Width = 8.5
mMargins.Left = 0.18
mMargins.Right = 0.18
mLabelSize.Width = 4.0
mSpacing.Width = 0.14
For God-knows-what reason, RES evaluates to {Width=1,Height=5} instead of {Width=2,Height=5}. I have evaluated the expressions on the right-side individually and as a whole and they correctly evaluate to {2,5}, but RES would never get correct value. Wonder what am I missing here.
EDIT
I have simplified the problem further. The following code will produce 2.0 if you QuickWatch the RHS, but the variable on the LHS will get 1.0 after you execute this line:
Dim X = Math.Floor(Math.Round(mPageSize.Width - mMargins.Left - mMargins.Right - mLabelSize.Width, 4) / (mLabelSize.Width + mSpacing.Width) + 1)
Time for MS to check it out?
EDIT 2
More info. The following gives correct results:
Dim Temp = mPageSize.Width - mMargins.Left - mMargins.Right - mLabelSize.Width
Dim X = Math.Floor(Temp / CDec(mLabelSize.Width + mSpacing.Width)) + 1
The problem is that the following expression evaluates to a value just below 1:
Math.Round(mPageSize.Width - mMargins.Left - mMargins.Right - mLabelSize.Width, 4) / (mLabelSize.Width + mSpacing.Width)
= 0.99999999985602739 (Double)
But what's the reason for that? The truth is that I don't know exactly. The MSDN does not offer enough information about the implementation of / but here's my guess:
Math.Round returns a Double with value 4.14. The right-hand side of the division is a Single. So you're dividing a Double by a Single. This results in a Double (see MSDN). So far, so good. The MSDN states that all integral data types are widened to Double before the division. Although Single is not an integral data type, this is probably what happens. And here is the problem. The widening does not seem to be performed on the result of the addition, but on its operands.
If you write
Dim sum = (mLabelSize.Width + mSpacing.Width) 'will be 4.14 Single
Math.Round(mPageSize.Width - mMargins.Left - mMargins.Right - mLabelSize.Width, 4) / sum
= 1 (Double)
Here sum is converted to double (resulting in 4.14) and everything is fine. But, if we convert both operands to double, then the conversion of 0.14 introduces some floating point error:
Dim dblLabelSizeWidth As Double = mLabelSize.Width ' will be 4.0
Dim dblSpacing As Double = mSpacing.Width ' will be 0.14000000059604645
The sum is slightly bigger than 4.14, resulting in a quotient slightly smaller than 1.
So the reason is that the conversion to double is not performed on the division's operand, but on the operand's operands, which introduces floating point errors.
You could overcome this problem by adding a small epsilon to the quotient before rounding off. Alternatively you might consider using a more precise data type such as Decimal. But at some point, there will also be floating-point errors with Decimal.
This is due to rounding error: you're taking the floor of a value that is very close to 2, but is less than 2 (while the mathematical value is 2). You should do all your computations with integers, or take rounding errors into account before using operations like floor (not always possible if you want the true value).
EDIT: Since vb.net has a Decimal datatype, you can also use it instead of integers. It may help in some cases like here: the base conversions for 0.18 and 0.14 (not representable exactly in binary) are avoided and the additions and subtractions will be performed exactly here, so that the operands of the division will be computed exactly. Thus, if the result of the division is an integer, you'll get it exactly (instead of possibly a value just below, like what you got with binary). But make sure that your inputs are already in decimal.
How do you access the decimal part of a number in MS Access? More specifically I want only the component after the decimal point, but not including the decimal point. This must also work for all whole numbers. I've seen this answered for other SQL engines, but they don't work in Access. I can't be much more specific than this because of the sensitive nature of what I'm actually working on.
For example given the following numbers the input is on the left and the output is on the right. Output can be either text or a number.
Source Correct Incorrect1 Incorrect2
10.0 0 0.0 .0
3.14159 14159 0.14159 .14159
45.65 65 0.65 .65
173.0 0 0.0 .0
143.15 15 0.15 .15
If I was using C# the following code would give me what I want:
private string getDecimalComponent(double input)
{
String strInput = input.ToString();
if (strInput.Contains('.'))
{
return strInput.Split('.')[1];
}
else
{
return "0";
}
}
Subtract the integer portion of the value.
Example:
4.25 - Int(4.25) = 0.25
Or, as a sample SQL expression:
SELECT
[myDecimalNumber],
[myDecimalNumber] - Int([myDecimalNumber]) as [rightOfDecimal]
FROM tableA
Something like:
SELECT 3.14%1 AS mycolumn from mytable
Depends on the circumstance. If this is, for instance, in a textbox, you can use InStr to find the decimal, and then use the Mid() function to get the number after it. If it's part of an arithmetic equation, then I would use the Int() function and subtract one number from the other to get the difference.
If you can elaborate on how it's being used, and in what context, I can edit my answer to give you more specifics.
EDIT: After more info came to light, try this:
Public Function GetParts(Temp1 as Double)
Temp2 = Int(Temp1)
Temp3 = Mid(Temp1, InStr(Temp1, ".") + 1)
MsgBox Temp2
MsgBox Temp3
End Function
A stable solution producing String containing decimal places from Double.
SELECT DecimalPlaces(MyColumn) FROM MyTable
where the above user-defined function contains the following code:
Function DecimalPlaces(ByVal value As Double) As String
Dim intPartLen As String
intPartLen = Len(CStr(CInt(Abs(value))))
If Len(CStr(Abs(value))) > intPartLen Then
DecimalPlaces = Mid(CStr(Abs(value)), intPartLen + 2)
Else
DecimalPlaces = "0"
End If
End Function
It avoids common mistakes, so it is
locale-independent - works with any decimal separator (it only assumes it is a single character)
preserves precision - avoids subtraction, takes the result only from string representation
Note: those Abs() calls are really required (hint: -0.1)