2010-05-12 32 views
9

在Redis(http://code.google.com/p/redis)中将双精度转换为整数以便将元素与元素相关联,以便将此元素进行排序。即使许多用户实际按整数排序(例如unix时间),该分数也是双打的。为了获得速度

当数据库被保存时,我们需要写这个双打ok磁盘。这是目前使用的内容:

snprintf((char*)buf+1,sizeof(buf)-1,"%.17g",val); 

此外还会检查无穷大和非数字条件,以便在最终的数据库文件中表示该条件。

不幸的是,将double转换为字符串表示法很慢。虽然我们在Redis中有一个以更快的方式将整数转换为字符串表示形式的函数。所以我的想法是检查一个double是否可以被转换成一个整数而不丢失数据,然后如果这是真的,则使用该函数将整数转换为一个字符串。

为了提供一个很好的加速,当然整数“等价”的测试必须是快速的。所以我使用了一种可能未定义的行为,但在实践中效果很好。类似的东西:

double x = ... some value ... 
if (x == (double)((long long)x)) 
    use_the_fast_integer_function((long long)x); 
else 
    use_the_slow_snprintf(x); 

在我的推理上面的double casting将double转换成long,然后返回到整数。如果范围适合,并且没有小数部分,则该数字将在转换后存活,并且与初始数字完全相同。因为我想确保这不会破坏某些系统中的某些东西,所以我加入了freenode上的#c,并受到很多侮辱;)因此,我现在正在尝试这里。

有没有一种标准的方法来做我想要做的事情,而不需要去ANSI C之外?否则,上述代码是否应该适用于当前Redis所针对的所有Posix系统?也就是说,Linux/Mac OS X/* BSD/Solaris现在正在运行的拱?

为了使代码更加完整,我可以添加的内容是在尝试执行演员之前明确检查双精度的范围。

谢谢你的帮助。

+0

侮辱侮辱,男人。我不知道答案,但我希望你找到答案。 – mmr 2010-05-12 17:06:03

+0

如果有帮助,http://stackoverflow.com/questions/638376/what-is-the-most-reliable-way-of-checking-if-a-floating-point-variable-is-an-inte was a在C#中检查这种方式。我还没有找到一个C版本。 – 2010-05-12 17:15:13

+0

或者,我可以使用modff()来检查小数部分是否为零?然后检查整体部分的范围是否在很长的范围内,如果属实,则施放它。 – antirez 2010-05-12 17:43:39

回答

6

也许一些旧的时尚定点数学可以帮助你。如果将双精度值转换为固定点值,您仍然可以获得小数精度,并且转换为字符串就像添加单个移位函数的整数一样容易。

另一个想法是推出自己的snprintf()函数。从double到int的转换本来就是由许多FPU单元支持的,所以它应该闪电般快速。将它转换为字符串也很简单。

只是一些随机的想法给你。

+1

谢谢Michael,哇,FPU支持这种转换?这确实是一个好消息。另外分离零件并独立打印它们的技巧很酷。谢谢这非常有帮助。 – antirez 2010-05-12 17:30:06

1

只要x在long long的范围内,我没有看到casts有问题。也许你应该检查一下modf()函数,它将double分解为其整数和小数部分。然后,您可以针对(double)LLONG_MIN和(double)LLONG_MAX添加检查以确认整体部分。虽然双精度可能会有困难。

但是在做任何事情之前,您是否确定它实际上是衡量其性能的瓶颈?整数值的百分比是否足够高,以至于真的会有所作为?

+2

非常感谢你,这已经实现,并导致保存数据库与许多双打两倍的速度。在snprintf()函数显示非常慢的分析会话之后开始优化... – antirez 2010-05-12 17:28:37

2

这样做的问题是比较不会按照您期望的方式进行。仅仅因为一个浮点值小于另一个浮点值并不意味着它作为整数的表示将小于另一个。另外,我看到你比较(先前)平等的一个双重价值之一。由于低位位的四舍五入和表示错误,您几乎永远不会想要做到这一点。

如果您只是在寻找某种类型的密钥来做类似哈希的事情,那么它可能会工作得很好。如果你真的关心哪些价值真的具有更大或更小的价值,那它就是一个坏主意。

+0

是的,我注意到了平等的双重比较。它可能是令人讨厌的来源,很难找到问题。它会在100次中使用99次。 – 2010-05-12 17:36:39

+1

你好泰德,如果你检查代码,我总是比较双打,但是在两步投射后会得到两倍。所以这个想法是,如果双精度匹配,那么它就能够通过这个“过滤器”而不会丢失信息。所以它的长表示可以被打印而不是它本身。 所以这样做的原因只是为了在双字串转换阶段获得速度。 – antirez 2010-05-12 17:40:20

+0

阅读有关比较双打,即使数字是完全相同的表示方式,这不起作用吗? 我知道,如果两个数字是从字符串表示或其他数学处理生成的,那么比较可能会失败,而数字仍然是epsilon-wise相同,但在我的具体情况下,我可以得到没有问题的假阴性,因为我会诉诸使用snprintf()的合理代码。 如果我正确理解问题,比较双打的问题是错误的否定,而不是误报。 – antirez 2010-05-12 17:57:16

0

你的测试是完全正常的(假设你已经分别处理了无穷大和NANs) - 这可能是你想要比较浮点数是否相等的极少数occaisions之一。它不会调用未定义的行为 - 即使x不在long long范围之外,您只会得到一个“实现定义的结果”,这里没关系。

只有在美中不足的是,负零将结束为正零(因为负零比较等于正零)。

+0

感谢caf,我在最近几小时研究了一些浮点数的表示。我也认为这是安全的。是的,在代码中已经明确检查了Nan和Infinity,所以应该是安全的。 为了让事情看起来更安全一些,我添加了#if来检查螳螂和长时间的精度匹配,所以只有在double有至少52位精度的情况下才会编译代码,然后按顺序进行显式测试检查double是否在long long不会溢出的范围内(并且此测试在52位范围内完成,因此我们保证它可以正常工作)。 Thx回复。 – antirez 2010-05-13 09:09:58

+1

范围测试是不必要的 - 如果你愿意,你甚至可以用'char'来代替。当'double'超出范围时,您得到的实现定义的结果只是在转换回'double'(C中的“溢出”仅发生在计算上,而不是转换)时不会相等。 – caf 2010-05-13 21:37:24