为什么计算结果在MongoDB的MapReduce中不正确

时间:2013-03-22 06:36:17

标签: mongodb mapreduce

我为mongodb写了一个mapreduce,但结果有一些问题

日期:

mongos> db.perGoods.find()

{ "_id" : ObjectId("514bf6428f43a9fee9cef526"), "id" : 1, "goods_id" : "1234", "keywords" : [   {   "keyword" : "lianyiqun",    "price" : 3.52 },   {   "keyword" : "nvzhuang",     "price" : 4.27 },   {   "keyword" : "chunkuan",     "price" : 3.12 },   {   "keyword" : "chaoliu",  "price" : 8.32 },   {   "keyword" : "duanzhuang",   "price" : 4.92 } ] }
{ "_id" : ObjectId("514bf65d8f43a9fee9cef527"), "id" : 2, "goods_id" : "5678", "keywords" : [   {   "keyword" : "lianyiqun",    "price" : 9.26 },   {   "keyword" : "nvzhuang",     "price" : 4.52 } ] }
{ "_id" : ObjectId("514bf6768f43a9fee9cef528"), "id" : 3, "goods_id" : "5612", "keywords" : [   {   "keyword" : "lianyiqun",    "price" : 7.42 },   {   "keyword" : "nvzhuang",     "price" : 6.52 } ] }
{ "_id" : ObjectId("514bf6968f43a9fee9cef529"), "id" : 4, "goods_id" : "9612", "keywords" : [   {   "keyword" : "lianyiqun",    "price" : 3.12 },   {   "keyword" : "nvzhuang",     "price" : 6.57 },   {   "keyword" : "chunzhuang",   "price" : 5.55 } ] }

地图功能:

mongos> var mapFunction = function() {
...                        for (var index = 0; index < this.keywords.length; index++) {
...                            var key = this.goods_id;
...                            var value = {
...                                          count: 1,
...                                          price: this.keywords[index].price
...                                        };
...                            emit(key, value);
...                        }
...                     };

reduce函数:

mongos> var reduceFunction = function(key, priceCountObjects) {
...                           reducedValue = { count: 0, sumprice: 0 };
... 
...                           for (var index = 0; index < priceCountObjects.length; index++) {
...                               reducedValue.count += priceCountObjects[index].count;
...                               reducedValue.sumprice += priceCountObjects[index].price;
...                           }
... 
...                           return reducedValue;
...                       };

代码:

mongos> db.perGoods.mapReduce(
...                      mapFunction,
...                      reduceFunction,
...                      { out: "map_reduce_test" }
...                    )
{
    "result" : "map_reduce_test",
    "timeMillis" : 5,
    "counts" : {
        "input" : 4,
        "emit" : 12,
        "reduce" : 4,
        "output" : 4
    },
    "ok" : 1,
}

结果:

mongos> db.map_reduce_test.find()
{ "_id" : "1234", "value" : { "count" : 5, "sumprice" : 24.15 } }
{ "_id" : "5612", "value" : { "count" : 2, "sumprice" : 13.94 } }
{ "_id" : "5678", "value" : { "count" : 2, "sumprice" : 13.78 } }
{ "_id" : "9612", "value" : { "count" : 3, "sumprice" : 15.240000000000002 } }
mongos> 

为什么最后的结果是15.240000000000002?

1 个答案:

答案 0 :(得分:0)

由于MongoDB中的map / reduce基于JavaScript,因此它使用JavaScript的number arithmetic,这实际上是双精度IEEE 754 floating point arithmetic

浮点数(基本上,有关详细信息,请参阅维基百科文章)存储为<​​/ p>

mantissa * base ^ exponent

在这种情况下,base为2(基数为10的浮点数称为decimal),如果值的精度有限,则某些数字(包括有理数)根本无法以此格式表示。

目前看来,最好的选择可能是在操作结束时进行四舍五入。不幸的是,并非所有货币都有两个小数位,因此国际化可能会变得很痛苦,并且仍然存在舍入变得重要的风险 - 确保你知道What Every Computer Scientist Should Know About Floating-Point Arithmetic

您也可以vote for this feature in 10gen's Jira