我在比较存储为双打的实数时遇到了一些问题。我认为这些问题最有可能是因为四舍五入的错误,但我不确定。如何比较存储为双打并在linq中测试的数字的最佳方法?
我从第三方来源获得时间作为字符串。这看起来像是从纪元开始的秒数将其转换为实时确定它是以秒为单位而不是毫秒。我把它转换为双重使用 double Time = Convert.ToDouble(“1549666889.6220000”);然后我使用linq从列表中提取包含此时间的所有条目
Infos.Where(x => x.StartTime <= starttime
&& x.EndTime >= starttime).OrderBy(x => x.StartTime).ToList();
我得到的结果似乎超出了我预期的比较边界。我希望返回的项目是我测试的时间在信息列表中项目的开始和结束时间之间的项目。
我得到类似的东西
(对不起,下一批应该是一个开始和结束时间表,但我不能在这里格式化表格布局)
开始时间结束时间1549665989.622097 1549666889.6221507 1549665989.6690228 1549666889.6790602 1549665989.8786857 1549666889.8817368 1549665989.8926628 1549666889.9037011
这些结果似乎是错误的,尤其是开始时间,因为它们应该小于给出的时间索引。
我认为这是一个四舍五入的问题,但不确定它是否是我的逻辑。如果它是一个舍入问题,我应该如何在LINQ中进行测试。
任何建议表示赞赏
我刚想到也许我应该将每个double值乘以10000000来删除小数并仅比较整数?这是一个好主意吗 ?
将"1549665989.622097"
之类的字符串转换为double会导致由于精度导致的错误。在这种情况下,转换后的双倍将是1549665989.6221
。
如果你的双精度错误是一个问题,你应该使用decimal数据类型:
decimal关键字表示128位数据类型。与其他浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算。
Convert.ToDecimal提供字符串所需的转换。结果将是没有精度错误的1549665989.622097
。
你确实意识到你将你的Where
的StartTime字符串转换成了一个双倍,你的OrderBy
再次转换了很多次你不是:OrderBy将第一个元素与第二个元素进行比较,第一个元素与第三个元素进行比较,以及第3和第3,第1和第4和第2与第4,第3和第4:你将你的弦转换为双打一遍又一遍。
记住这种转换并重新使用转换后的值会不会更有效率?
因为我们正在转换第三方数据,为什么不将它转换为代表某个时间点的适当对象:System.DateTime
?
编写类Info的两个扩展函数:
static class InfoExtensions
{
public static DateTime StartDateTime(this Info info)
{
return info.startTime.ToDateTime();
}
public static DateTime EndDateTime(this Info info)
{
return info.endTime.ToDateTime();
}
private static DateTime ToDateTime(this string date3rdParty)
{
// ask from your 3rd party what the value means
// for instance: seconds since some start epoch time:
static DateTime epochTime = new DateTime(...)
double secondsSinceEpochTime = Double.Parse(date3rdParty);
return epochTime.AddSeconds(secondsSinceEpochTime);
}
}
用法:
DateTime startTime = ...
var result = Infos.Select(info => new
{
StartTime = info.StartTime.StartDatetime(),
EndTime = info.EndTime.EndDateTime(),
// select the Info properties you actually plan to use:
...
// or select the complete Info:
Info = info,
})
.Where(info => info.StartTime <= startTime && startTime <= info.EndTime)
.OrderBy(info => info.StartTime)
// Only if you prefer to throw away your converted StartTime / EndTime:
.Select(info => info.Info);
可能是您的第三方时间的精度与DateTime的精度不同,并且您希望获得最终精度。在这种情况下,请考虑将其字符串转换为DateTime.Ticks
,然后使用此Ticks创建新的DateTime对象。由于Ticks是整数,因此转换的麻烦会减少
你应该在separation of concerns上做更多的工作。如果你将你的第三方代表他们对日期的想法(从某个纪元时间开始的一些字符串表示,从你希望拥有它的方式)(可能是System.DateTime),那么你就不会有这个问题了。
如果你将info
类与info
类分开,那么你的代码将更易于维护,因为你只有一个地方将其信息属性转换为你的信息属性。如果将来他们添加了您不使用的属性,您将不会注意到它。如果他们决定改变他们对日期的想法,例如通过使用不同的纪元时间,或者可能使用System.DateTime,那么只有一个地方您将不得不更改您的信息。另外:如果有第四方信息:只有一个地方你必须转换。
分离是有效的:无论您使用属性StartTime的频率如何,转换只进行一次。例如,如果将来您希望将所有Infos按相同日期分组。
分离也更容易测试:大多数代码都适用于您自己的转换信息类。只有一小段代码会将其信息转换为您对信息的想法。你可以使用你的信息类来测试大多数代码,它们只是你需要测试转换的地方:一旦你知道转换是好的,你就再也不用担心它了
创建一个MyNamespace.Info类,它有一个构造函数thirdPartyNamespace.Info:
class MyInfo
{
public DateTime StartTime {get; set;}
public DateTime EndTime {get; set;}
... // other info properties you actually plan to use
// Constructors:
public MyInfo() { } // default constructor
public MyInfo(ThirdParyNameSpace.Info info)
{
this.StartTime = info.StartTime.ToDateTime();
this.EndTime = info.EndTime.ToDateTime();
...
}
}
您是否看到从第四方添加对Info的支持是多么容易?或者,如果第三方信息发生变化,或者您需要更多属性(或更少),那么变化有多大?
几乎所有代码都可以使用本地信息类进行测试。只需要一个测试类来测试第三方信息是否正确转换为您的信息。