Question

I am having an issue when converting Single values to Double values.

The singles provided by a BitStream are simple 2 to 6 place decimal numbers, in many cases, as simple as 0.4, 0.94, 0.6, etc. (I should note, the documentation I received states they are Floats(in Java), which from my understanding is the same as a Single in .NET.

I ultimately need these values as doubles as they will be used as coordinates for a Point3D Object (X, Y and Z), and ultimately used in the API of other applications where a Double is required also.

However, when I perform the conversion using CDbl(valueAsSingle) or Ctype(valueAsSingle, Double) the number has extra decimal places added to the Double, out in the 9th and later decimal place. This is causing issues in the applications that finally need to work with these values.

First, I'm curious why this is happening? Secondly, could issues arise if I simply convert the Single to a String, and then perform a Double.TryParse(valueAsString)

For reference, here is a very simple example.

Sub Main()
    Dim SingleX As Single = 0.4
    Dim SingleY As Single = 0.94
    Dim SingleZ As Single = 0.6

    Console.WriteLine(String.Concat("SX: ", SingleX, ControlChars.NewLine, "SY: ", SingleY, ControlChars.NewLine, "SZ: ", SingleZ, ControlChars.NewLine))

    Dim DoubleX As Double = CDbl(SingleX)
    Dim DoubleY As Double = CDbl(SingleY)
    Dim DoubleZ As Double = CDbl(SingleZ)

    Console.WriteLine(String.Concat("DX: ", DoubleX, ControlChars.NewLine, "DY: ", DoubleY, ControlChars.NewLine, "DZ: ", DoubleZ))

    Console.ReadLine()
End Sub

The results of which are

SX: 0.4
SY: 0.94
SZ: 0.6

DX: 0.400000005960464
DY: 0.939999997615814
DZ: 0.600000023841858
Was it helpful?

Solution

Ok, so with a pointer from a colleague, I've found this Wikipedia article that talks about Accuracy issues with Single Precision. I've got to admit my eyes glaze when reading it, but perhaps you will have a better time of it.

I can't talk to your specific scenario, but ToStringing/Converting shouldn't have much of an issue. Alternatively you could round it as per Imrans answer.

OTHER TIPS

Use following

Dim DoubleX As Double = Math.Round(Convert.ToDouble(SingleX),2)
Dim DoubleY As Double = Math.Round(Convert.ToDouble(SingleY),2)
Dim DoubleZ As Double = Math.Round(Convert.ToDouble(SingleZ),2)

2 is the int digit to indicate that how many fraction you want

so

above code will return :

DX: 0.4
DY: 0.94
DZ: 0.6

I Assume that you are using .net 4.0

Leave the values alone (resists the Math.Round() temptation) and deal with the output. After years of trying I ended with this (translated from C# to VB.NET via http://www.developerfusion.com/tools/convert/csharp-to-vb ):

<System.Runtime.CompilerServices.Extension> _
Public Shared Function Nice(x As Double, significant_digits As Integer) As String
    'Check for special numbers and non-numbers
    If Double.IsInfinity(x) OrElse Double.IsNaN(x) Then
        Return x.ToString()
    End If
    ' extract sign so we deal with positive numbers only
    Dim sign As Integer = Math.Sign(x)
    x = Math.Abs(x)
    Dim fmt As String
    x = Math.Round(x, 15)
    If x = 0 Then
        fmt = New String("#"C, significant_digits - 1)
        Return String.Format("{0:0." & fmt & "}", x)
    End If
    ' get scientific exponent, 10^3, 10^6, ...
    Dim sci As Integer = CInt(Math.Floor(Math.Log(x, 10) / 3)) * 3
    ' biases decimal when exponent is negative
    ' example 0.123 shows as 0.123 instead of 123e-3
    If sci<0 Then 
        sci += 3
    End If
    ' scale number to exponent found
    x = x * Math.Pow(10, -sci)
    ' find number of digits to the left of the decimal
    Dim dg As Integer = CInt(Math.Floor(Math.Log(x, 10))) + 1
    ' adjust decimals to display
    Dim decimals As Integer = Math.Min(significant_digits - dg, 15)
    ' format for the decimals
    fmt = New String("#"C, decimals)
    Dim num = Math.Round(x, decimals)
    If sci = 0 Then
        'no exponent
        Return String.Format("{0}{1:0." & fmt & "}", If(sign < 0, "-", String.Empty), num)
    End If
    Return String.Format("{0}{1:0." & fmt & "}e{2}", If(sign < 0, "-", String.Empty), num, sci)
End Function

Here are some examples:

x                       Nice(x,4)
0.9f                    0.9
0.96666666f             0.9667
96666f                  96.67e3
9666666f                9.667e6
0.939999997615814e-5f   0.0094e-3
0.939999997615814f      0.94
0.939999997615814e-5f   0.94e3

Try this: Convert a "converted to string" single to double.

Dim SingleX as single = 0.4
Dim SingleX as single = 0.94
Dim SingleX as single = 0.8
Dim DoubleX, DoubleY, DoubleZ as double
Double.TryParse(SingleX.tostring, DoubleX)
Double.TryParse(SingleY.tostring, DoubleY)
Double.TryParse(SingleZ.tostring, DoubleZ)
DoubleX = 0.4
DoubleY = 0.94
DoubleZ = 0.6
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top