如何正确测试浮点数或双倍的质量和其他比较



我在比较存储为双精度的实数时遇到了一些问题。 我认为问题很可能是由舍入误差引起的,但我不确定。 比较存储为双精度并在 linq 中测试的数字的最佳方法是什么?

我从第三方来源获得一个字符串的时间。 这看起来像是纪元的几秒钟 将其转换为实时可以确定它是以秒为单位而不是毫秒。 我使用
双倍时间 = 转换.ToDouble("1549666889.6220000")将其隐蔽为双倍; 然后我使用 linq 从列表中提取这次包含的所有条目

Infos.Where(x => x.StartTime <= starttime                                                                 
&& x.EndTime >= starttime).OrderBy(x => x.StartTime).ToList();

我得到的结果似乎超出了我预期的比较界限。 我预计返回的项目是im测试的时间介于信息列表中项目的开始时间和结束时间之间的项目。

我得到类似的东西

(抱歉,下一批应该是开始和结束时间的表格,但我无法在此处将其格式化为表格布局)

开始时间结束时间 1549665989.622097 1549666889.6221507 1549665989.6690228 1549666889.6790602 1549665989.8786857
1549666889.8817368 1549665989.8926628 1549666889.9037011

这些结果似乎是错误的,尤其是开始时间,因为它们应该小于IM给出的时间指数。

我认为这是一个四舍五入的问题,但不确定是那个还是我的逻辑。 如果是舍入问题,我应该如何在 LINQ 中进行测试。

任何建议表示赞赏。

我只是想到也许我应该将每个双精度值乘以 10000000 以删除小数并仅比较整数? 这是个好主意吗?

将像"1549665989.622097"这样的字符串转换为双精度会导致错误。在这种情况下,转换后的双精度将1549665989.6221

如果双精度的精度错误是一个问题,则应使用十进制数据类型:

decimal 关键字指示 128 位数据类型。与其他浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算。

Convert.ToDecimal 提供从字符串进行所需的转换。结果将被1549665989.622097,没有精度误差。

您的转换效率低下

您确实意识到您将 StartTime 字符串转换为双精度值以表示Where,并且很多时候都是为了您的OrderBy不是:OrderBy 会将第一个元素与第二个元素进行比较,第一个元素与第三个元素进行比较,第二个元素与第三个元素进行比较,第一个元素与第 4 个元素进行比较,第二个元素与第 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.Ticks,然后使用此 Ticks 创建新的 DateTime 对象。由于即时报价是整数,因此转换时遇到的麻烦会更少

关注点分离

您应该在关注点分离方面做更多的工作。如果你将你的第三方表示他们的日期想法的方式(一些自某个纪元时间以来的秒的字符串表示)与你想要的方式(可能是System.DateTime)分开,那么你就不会有这个问题。

如果将它们的info类与info类分开,则代码将更易于维护,因为只有一个位置将其信息属性转换为信息属性。如果将来他们添加了您不使用的属性,您不会注意到它。如果他们决定改变他们对日期的想法,例如通过使用不同的纪元时间,或者可能使用 System.DateTime,则只有一个地方您必须更改您的信息。另外:如果有第四方信息:只有一个地方需要转换。

分离是有效的:无论您使用属性 StartTime 的频率如何,转换都只执行一次。例如,如果将来您想按同一日期分组所有信息。

分离也更容易测试:您的大多数代码将与您自己的转换信息类一起使用。只有一小段代码会将他们的信息转换为您的信息。您可以使用 info 类测试大多数代码,并且只有一个地方需要测试转换:一旦您知道转换是好的,您将永远不必再担心它

创建一个类 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();
...
}
}

您是否看到从第四方添加对信息的支持是多么容易?或者,如果第三方信息发生变化,或者您需要更多(或更少)的属性,则变化有多小?

几乎所有代码都可以使用本地信息类进行测试。只需要一个测试类来测试第三方信息是否正确转换为您的信息。

最新更新