Преобразование одного в двойной
У меня возникла проблема при преобразовании одинарных значений в двойные значения.
Синглы, предоставляемые BitStream, представляют собой простые от 2 до 6 десятичных чисел, во многих случаях такие простые, как 0,4, 0,94, 0,6 и т. Д. (Следует отметить, что в полученной мной документации указывается, что они являются числами с плавающей точкой (в Java), что из мое понимание такое же, как сингл в.NET.
В конечном итоге мне нужны эти значения в виде двойных чисел, так как они будут использоваться в качестве координат для объекта Point3D (X, Y и Z) и, в конечном счете, использоваться в API других приложений, где также требуется двойное значение.
Тем не менее, когда я выполняю преобразование с использованием CDbl(valueAsSingle) или Ctype(valueAsSingle, Double), в число добавляются дополнительные десятичные разряды, добавленные в Double, в девятом и последующем десятичных знаках. Это вызывает проблемы в приложениях, которые наконец должны работать с этими значениями.
Во-первых, мне любопытно, почему это происходит? Во-вторых, могут возникнуть проблемы, если я просто преобразую Single в строку, а затем выполню Double.TryParse(valueAsString)
Для справки приведу очень простой пример.
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
Результаты которых
SX: 0.4
SY: 0.94
SZ: 0.6
DX: 0.400000005960464
DY: 0.939999997615814
DZ: 0.600000023841858
3 ответа
Итак, по указанию коллеги я нашел эту статью в Википедии, в которой говорится о проблемах точности с Single Precision. Я должен признать, что мои глаза глазурят при чтении, но, возможно, вам будет лучше.
Я не могу говорить с вашим конкретным сценарием, но ToStringing/Converting не должен вызывать особых проблем. В качестве альтернативы вы можете округлить его согласно ответу Имрана.
Используйте следующее
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 - это целое число, указывающее, сколько дроби вы хотите
так
Код выше вернется:
DX: 0.4
DY: 0.94
DZ: 0.6
Я предполагаю, что вы используете.net 4.0
Оставьте значения в покое (сопротивляется Math.Round()
искушение) и разобраться с выходом. После многих лет попыток я закончил с этим C#
в VB.NET
через 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
Вот некоторые примеры:
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
Попробуйте это: преобразовать одинарное "преобразованное в строку" в двойное.
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