在几乎相同的视图中浮点精度不同?

时间:2011-11-11 14:50:54

标签: couchdb floating-accuracy

使用下一个bash脚本创建数据库:

#! /bin/bash
curl -X PUT http://127.0.0.1:5984/sales
IFS=$';'
vals=`cat sales_upload.json`
for i in $vals 
do
    curl -X POST http://127.0.0.1:5984/sales -H "Content-Type: application/json" -d $i
done
unset IFS

和资源文件:

{
    "Type" : "customer",
    "LastName" : "Welsh", 
    "FirstName" : "Jim",
    "Address" : "340 West 50th Street, New York, NY",
    "TotalSpent" : 734.34
};
{
    "Type" : "customer",
    "LastName" : "Zuch", 
    "FirstName" : "Bo",
    "Address" : "116 10th Avenue, New York, NY",
    "TotalSpent" : 1102.47
};
{
    "Type" : "customer",
    "LastName" : "Libby", 
    "FirstName" : "Joe",
    "Address" : "611 Fifth Avenue, New York, NY",
    "TotalSpent" : 290.01
};
{
    "Type" : "customer",
    "LastName" : "Grant", 
    "FirstName" : "Sue",
    "Address" : "7 West 55th Street, Manhattan, NY",
    "TotalSpent" : 430.83
};
{
    "Type" : "salesman",
    "LastName" : "Green", 
    "FirstName" : "Gwen",
    "Level" : 1
};
{
    "_id" : "_design/logic",
    "language" : "javascript",
    "views" :
    {
        "customers": {
            "map" : "function(doc) { if (doc.Type == 'customer')  emit(null, {LastName: doc.LastName, FirstName: doc.FirstName, Address: doc.Address}) }"
        },
        "total_purchases": {
            "map" : "function(doc) { if (doc.Type == 'customer')  emit(null, doc.TotalSpent) }",
            "reduce" : "function(keys, values) { return sum(values) }"
        }
    }
}
我打电话的时候 curl -X GET http://127.0.0.1:5984/sales/_design/logic/_view/total_purchases

我得到:

  

{“rows”:[{“key”:null,“value”: 2557.65 }]}

但如果我在total_purchases中将emit的第一个参数更改为emit(doc.LastName,doc.TotalSpent),那么我将得到:

  

{“rows”:[{“key”:null,“value”: 2557.6499999999996 }]}

为什么会这样?

1 个答案:

答案 0 :(得分:1)

您的答案之间的差异是由于您更改了视图功能。要发出的第一个参数确定如何构建视图索引。在第一种情况下,所有发射的值都将存储在“null”键下。现在,您可以使用第二个示例将索引分布在不同的键上,即客户的姓氏。

因此,couchdb中的内部btree在视图之间会有所不同。那么为什么你会得到不同的结果呢?

CouchDB使用增量map / reduce。你可以在这里阅读: http://damienkatz.net/2008/02/incremental_map.html

从那篇文章中Damien指出:

  

为了使增量Map / Reduce成为可能,Reduce函数不仅要求它必须是引用透明的,而且它还必须对于数组值输入是可交换的和关联的,以便能够减少它自己的输出并获得同样的答案,像这样:

     

f(键,值)== f(键,[f(键,值)])

     

reduce函数的这个要求允许CouchDB将中间减少直接存储到btree索引的内部节点中,并且视图索引更新和检索将具有对数成本。它还允许索引跨机器分布,并在查询时以对数成本减少。

     

增量设计使得可以使用map / reduce实时查询大型分区群集,而不必等待整个map / reduce作业完成或有过时的,偶尔更新的索引。缺点是以关联和可交换的方式编写Reduce函数可能更难。

因此我假设发生的事情是在第一个视图中,因为它们都在同一个键下,所以没有存储的中间缩减。在第二个视图中,存储临时总和。您可能会看到浮点数存储在这些中间总和中的方式不同。看这里: Is floating point math broken?

两条建议可以帮助您解决此问题。首先是使用对Erlang版本的reduce函数的“内置”调用。见这里:

http://wiki.apache.org/couchdb/Built-In_Reduce_Functions

电话略有不同:“减少”:“_ sum”

其次,可以转换将float作为整数发出,如下所示: Is floating point math broken?

希望这有帮助。