我使用SQLite数据库在.NET项目中存储双精度浮点值。
我遇到了NaN和无穷大值的一个众所周知的问题,我正在尝试通过为这些值存储字符串“NaN”,“+ Inf”,“ - Inf”并将它们转换回相应的双值来解决问题。需要。我正在使用System.Data.SQLite
类在我的项目中实现SQLite。
现在我看到了解决问题的不同方法。一种工作方法如下:我可以使用TEXT
类型创建列。当我为每个值执行此操作时,即使有效的双数也会存储为字符串:
创建表格:
Using cmd As New SQLiteCommand("CREATE TABLE IF NOT EXISTS Test (vals TEXT);")
...
Using cmd As New SQLiteCommand("INSERT INTO Test (vals) VALUES(@v);")
...
p.Value = SQLite_DoubleToValue(Double.PositiveInfinity)
双倍到价值转换方法:
Private Function SQLite_DoubleToValue(D As Double) As Object
If Double.IsNaN(D) Then
Return "NaN"
ElseIf D > 1.79769313486E+308 Then 'Some rounding, everything above this is treated as infinity by SQLite
Return "+Inf"
ElseIf D < -1.79769313485E+308 Then 'Same here
Return "-Inf"
Else
Return D 'Return the actual double value, boxed in an Object
End If
End Function
回读:
Using cmd As New SQLiteCommand("SELECT * FROM Test;")
Dim rdr As SQLiteDataReader = cmd.ExecuteReader
rdr.Read()
Dim val As Double = SQLite_ValueToDouble(rdr.GetValue(0))
双转换方法的第二个值:
Private Function SQLite_ValueToDouble(V As Object) As Double
If TypeOf (V) Is String Then
Dim s As String = CStr(V)
If s = "NaN" Then
Return Double.NaN
ElseIf s = "+Inf" Then
Return Double.PositiveInfinity
ElseIf s = "-Inf" Then
Return Double.NegativeInfinity
Else 'Value can only be a number representation here
Dim val As Double = Double.NaN
Double.TryParse(s, val) '<- I don't like to do this conversion
Return val
End If
ElseIf TypeOf (V) Is Double Then '<- This never happens when the column is created as TEXT
Return CDbl(V)
Else
Return Double.NaN
End If
End Function
正如我在后一种转换方法中所指出的,我不喜欢甚至有效数字存储为字符串的事实。当我声明TEXT列而不是REAL列时数据库的大小增加,并且由于SQLite没有真正强制执行数据类型,所以我应该能够将文本存储在数字列中。事实上我可以。
如果我像这样创建我的表:
Using cmd As New SQLiteCommand("CREATE TABLE IF NOT EXISTS Test (vals REAL);")
...
Using cmd As New SQLiteCommand("INSERT INTO Test (vals) VALUES(@v);")
...
p.Value = SQLite_DoubleToValue(Double.PositiveInfinity)
并在数据库浏览器中检查我的数据库中的SQLite我看到了:
因此文本很好地存储在数字列中。
但是,如果我在.NET中使用GetValue
读回来我没有得到字符串,但我得到的值为0.0(Double)。
似乎SQLite API或System.Data.SQLite
实现会在自动遇到实际列时将每个值转换为double,即使使用GetValue
而不是GetDouble
也是如此。它可能不是API,因为数据库浏览器正确读取字符串值。
我是否会错过一些明显的东西,或者是否有一些错误,也许是一个修复程序? 我希望看到的行为是,如果我在表中写了一个double值,GetValue会返回一个double值(当然是盒装在一个对象中),如果我在列中写了一个字符串,则返回一个字符串。有没有办法实现这个目标?
我已经阅读了BindAndGetAllAsText
标志,但我发现问题因为a)所有内容都被写成文本到数据库中b)字符串表示依赖于操作系统语言,这可能会导致阅读时出现问题数据回到不同的计算机上。
VB.NET和C#中的答案都非常受欢迎,因为这里不是特定于语言的问题。
答案 0 :(得分:1)
我通过将三个特定的double值定义为表示三种特殊状态的常量来解决问题:
Public Const NaNValue As Double = 1.7E308
Public Const PInfValue As Double = 1.71E308
Public Const NInfValue As Double = -1.71E308
我还使用类似于我在问题中展示的函数来将数据库值转换为double值,反之亦然:
Protected Shared Function DoubleToValue(D As Double) As Double
If Double.IsNaN(D) Then
Return NaNValue
ElseIf D >= PInfValue Then
Return PInfValue
ElseIf D <= NInfValue Then
Return NInfValue
Else
Return D
End If
End Function
Protected Shared Function ValueToDouble(V As Double) As Double
If V = NaNValue Then
Return Double.NaN
ElseIf V = PInfValue Then
Return Double.PositiveInfinity
ElseIf V = NInfValue Then
Return Double.NegativeInfinity
Else
Return V
End If
End Function
这允许我直接从数据库读取和写入双值,同时保持特殊值的正确行为。您当然会将您定义的单个数字松散为常量,并稍微限制双值范围。在大多数用例中你应该没问题,你甚至可以比我在这里更进一步地移动限制。