1
0
Fork 0

Merge branch 'devel' of github.com:arangodb/arangodb into devel

This commit is contained in:
Willi Goesgens 2015-02-24 13:12:00 +01:00
commit 597ce40879
17 changed files with 1007 additions and 192 deletions

View File

@ -1,5 +1,11 @@
v2.5.0-alpha2 (2015-02-16)
-------------------
--------------------------
* fixed issue #1230: API: document/col-name/_key and cursor return different floats
v2.5.0-beta1 (2015-02-23)
-------------------------
* front-end: dashboard tries not to (re)load statistics if user has no access

View File

@ -0,0 +1,451 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite for fpconv.cpp
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2015, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include <boost/test/unit_test.hpp>
#include "Basics/StringBuffer.h"
#include "Basics/fpconv.h"
#include "Basics/json.h"
#include "Basics/string-buffer.h"
using namespace triagens::basics;
// -----------------------------------------------------------------------------
// --SECTION-- setup / tear-down
// -----------------------------------------------------------------------------
struct CFpconvSetup {
CFpconvSetup () {
BOOST_TEST_MESSAGE("setup fpconv");
}
~CFpconvSetup () {
BOOST_TEST_MESSAGE("tear-down fpconv");
}
};
// -----------------------------------------------------------------------------
// --SECTION-- test suite
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief setup
////////////////////////////////////////////////////////////////////////////////
BOOST_FIXTURE_TEST_SUITE(CFpconvTest, CFpconvSetup)
////////////////////////////////////////////////////////////////////////////////
/// @brief test nan
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_nan) {
char out[24];
double value;
int length;
value = NAN;
BOOST_CHECK_EQUAL(true, std::isnan(value));
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("NaN"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("NaN"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test infinity
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_inf) {
char out[24];
double value;
int length;
value = INFINITY;
BOOST_CHECK_EQUAL(false, std::isfinite(value));
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("inf"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("inf"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test huge val
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_huge_val) {
char out[24];
double value;
int length;
value = HUGE_VAL;
BOOST_CHECK_EQUAL(false, std::isfinite(value));
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("inf"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("inf"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test huge val
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_huge_val_neg) {
char out[24];
double value;
int length;
value = -HUGE_VAL;
BOOST_CHECK_EQUAL(false, std::isfinite(value));
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("-inf"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("-inf"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test zero
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_zero) {
char out[24];
double value;
int length;
value = 0;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("0"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("0"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test zero
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_zero_neg) {
char out[24];
double value;
int length;
value = -0;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("0"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("0"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test high
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_high) {
char out[24];
double value;
int length;
value = 4.32e261;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("4.32e+261"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("4.32e+261"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test low
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_low) {
char out[24];
double value;
int length;
value = -4.32e261;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("-4.32e+261"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("-4.32e+261"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test small
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_small) {
char out[24];
double value;
int length;
value = 4.32e-261;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("4.32e-261"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("4.32e-261"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test mchacki's value
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_mchacki1) {
char out[24];
double value;
int length;
value = 1.374;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("1.374"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("1.374"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test mchacki's value
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_mchacki2) {
char out[24];
double value;
int length;
value = 56.94837631946843;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("56.94837631946843"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("56.94837631946843"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test one third roundtrip
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_mchacki2_roundtrip) {
double value;
value = 56.94837631946843;
TRI_string_buffer_t buffer;
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
auto json = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value);
TRI_StringifyJson(&buffer, json);
BOOST_CHECK_EQUAL(std::string("56.94837631946843"), std::string(buffer._buffer, buffer._current - buffer._buffer));
auto json2 = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, buffer._buffer, nullptr);
BOOST_CHECK_EQUAL(TRI_JSON_NUMBER, json2->_type);
BOOST_CHECK_EQUAL(value, json2->_value._number);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json2);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
TRI_DestroyStringBuffer(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test one third
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_one_third) {
char out[24];
double value;
int length;
value = 1.0 / 3.0;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("0.3333333333333333"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("0.3333333333333333"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test one third roundtrip
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_one_third_roundtrip) {
double value;
value = 1.0 / 3.0;
TRI_string_buffer_t buffer;
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
auto json = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value);
TRI_StringifyJson(&buffer, json);
BOOST_CHECK_EQUAL(std::string("0.3333333333333333"), std::string(buffer._buffer, buffer._current - buffer._buffer));
auto json2 = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, buffer._buffer, nullptr);
BOOST_CHECK_EQUAL(TRI_JSON_NUMBER, json2->_type);
BOOST_CHECK_EQUAL(value, json2->_value._number);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json2);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
TRI_DestroyStringBuffer(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test 0.4
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_04) {
char out[24];
double value;
int length;
value = 0.1 + 0.3;
length = fpconv_dtoa(value, out);
BOOST_CHECK_EQUAL(std::string("0.4"), std::string(out, length));
StringBuffer buf(TRI_UNKNOWN_MEM_ZONE);
buf.appendDecimal(value);
BOOST_CHECK_EQUAL(std::string("0.4"), std::string(buf.c_str(), buf.length()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test 0.4
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_04_roundtrip) {
double value;
value = 0.1 + 0.3;
TRI_string_buffer_t buffer;
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
auto json = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value);
TRI_StringifyJson(&buffer, json);
BOOST_CHECK_EQUAL(std::string("0.4"), std::string(buffer._buffer, buffer._current - buffer._buffer));
auto json2 = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, buffer._buffer, nullptr);
BOOST_CHECK_EQUAL(TRI_JSON_NUMBER, json2->_type);
BOOST_CHECK_EQUAL(value, json2->_value._number);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json2);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
TRI_DestroyStringBuffer(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test big roundtrip
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_high_roundtrip) {
double value;
value = 4.32e261;
TRI_string_buffer_t buffer;
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
auto json = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value);
TRI_StringifyJson(&buffer, json);
BOOST_CHECK_EQUAL(std::string("4.32e+261"), std::string(buffer._buffer, buffer._current - buffer._buffer));
auto json2 = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, buffer._buffer, nullptr);
BOOST_CHECK_EQUAL(TRI_JSON_NUMBER, json2->_type);
BOOST_CHECK_EQUAL(value, json2->_value._number);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json2);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
TRI_DestroyStringBuffer(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test small roundtrip
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE (tst_value_low_roundtrip) {
double value;
value = -4.32e261;
TRI_string_buffer_t buffer;
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
auto json = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value);
TRI_StringifyJson(&buffer, json);
BOOST_CHECK_EQUAL(std::string("-4.32e+261"), std::string(buffer._buffer, buffer._current - buffer._buffer));
auto json2 = TRI_Json2String(TRI_UNKNOWN_MEM_ZONE, buffer._buffer, nullptr);
BOOST_CHECK_EQUAL(TRI_JSON_NUMBER, json2->_type);
BOOST_CHECK_EQUAL(value, json2->_value._number);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json2);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
TRI_DestroyStringBuffer(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate tests
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_SUITE_END ()
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End:

View File

@ -704,12 +704,12 @@ BOOST_AUTO_TEST_CASE (tst_doubles) {
value = n * n * n * n;
TRI_ClearStringBuffer(&sb);
TRI_AppendDoubleStringBuffer(&sb, value);
BOOST_CHECK_EQUAL("3575783498001355000000", sb._buffer);
BOOST_CHECK_EQUAL("3575783498001355400000", sb._buffer);
value *= -1.0;
TRI_ClearStringBuffer(&sb);
TRI_AppendDoubleStringBuffer(&sb, value);
BOOST_CHECK_EQUAL("-3575783498001355000000", sb._buffer);
BOOST_CHECK_EQUAL("-3575783498001355400000", sb._buffer);
TRI_DestroyStringBuffer(&sb);
}

View File

@ -40,6 +40,7 @@ add_executable(
Basics/conversions-test.cpp
Basics/csv-test.cpp
Basics/files-test.cpp
Basics/fpconv-test.cpp
Basics/json-test.cpp
Basics/json-utilities-test.cpp
Basics/hashes-test.cpp

View File

@ -456,6 +456,58 @@ describe ArangoDB do
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1501)
end
end
################################################################################
## floating points
################################################################################
context "fetching floating-point values:" do
before do
@cn = "users"
ArangoDB.drop_collection(@cn)
@cid = ArangoDB.create_collection(@cn, false)
ArangoDB.post("/_api/document?collection=#{@cid}", :body => "{ \"_key\" : \"big\", \"value\" : 4e+262 }")
ArangoDB.post("/_api/document?collection=#{@cid}", :body => "{ \"_key\" : \"neg\", \"value\" : -4e262 }")
ArangoDB.post("/_api/document?collection=#{@cid}", :body => "{ \"_key\" : \"pos\", \"value\" : 4e262 }")
ArangoDB.post("/_api/document?collection=#{@cid}", :body => "{ \"_key\" : \"small\", \"value\" : 4e-262 }")
end
after do
ArangoDB.drop_collection(@cn)
end
it "fetching via cursor" do
cmd = api
body = "{ \"query\" : \"FOR u IN #{@cn} SORT u._key RETURN u.value\" }"
doc = ArangoDB.log_post("#{prefix}-float", cmd, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc.parsed_response['id'].should be_nil
result = doc.parsed_response['result']
result.length.should eq(4)
result[0].should eq(4e262);
result[1].should eq(-4e262);
result[2].should eq(4e262);
result[3].should eq(4e-262);
doc = ArangoDB.get("/_api/document/#{@cid}/big")
doc.parsed_response['value'].should eq(4e262)
doc = ArangoDB.get("/_api/document/#{@cid}/neg")
doc.parsed_response['value'].should eq(-4e262)
doc = ArangoDB.get("/_api/document/#{@cid}/pos")
doc.parsed_response['value'].should eq(4e262)
doc = ArangoDB.get("/_api/document/#{@cid}/small")
doc.parsed_response['value'].should eq(4e-262)
end
end
end

View File

@ -338,6 +338,7 @@ UnitTests_basics_suite_SOURCES = \
UnitTests/Basics/conversions-test.cpp \
UnitTests/Basics/csv-test.cpp \
UnitTests/Basics/files-test.cpp \
UnitTests/Basics/fpconv-test.cpp \
UnitTests/Basics/json-test.cpp \
UnitTests/Basics/json-utilities-test.cpp \
UnitTests/Basics/hashes-test.cpp \

View File

@ -28,6 +28,7 @@
////////////////////////////////////////////////////////////////////////////////
#include "Aql/Functions.h"
#include "Basics/fpconv.h"
#include "Basics/JsonHelper.h"
#include "Utils/Exception.h"
@ -115,38 +116,48 @@ AqlValue Functions::Length (triagens::arango::AqlTransaction* trx,
if (json != nullptr) {
switch (json->_type) {
case TRI_JSON_UNUSED:
case TRI_JSON_NULL:
case TRI_JSON_NULL: {
length = strlen("null");
break;
}
case TRI_JSON_BOOLEAN:
case TRI_JSON_BOOLEAN: {
length = (json->_value._boolean ? strlen("true") : strlen("false"));
break;
}
case TRI_JSON_NUMBER:
try {
std::string toString = std::to_string(json->_value._number);
length = toString.size();
case TRI_JSON_NUMBER: {
if (std::isnan(json->_value._number) ||
! std::isfinite(json->_value._number)) {
// invalid value
length = strlen("null");
}
catch (...) {
else {
// convert to a string representation of the number
char buffer[24];
length = static_cast<size_t>(fpconv_dtoa(json->_value._number, buffer));
}
break;
}
case TRI_JSON_STRING:
case TRI_JSON_STRING_REFERENCE:
case TRI_JSON_STRING_REFERENCE: {
// return number of characters (not bytes) in string
length = TRI_CharLengthUtf8String(json->_value._string.data);
break;
}
case TRI_JSON_OBJECT:
case TRI_JSON_OBJECT: {
// return number of attributes
length = json->_value._objects._length / 2;
break;
}
case TRI_JSON_ARRAY:
case TRI_JSON_ARRAY: {
// return list length
length = TRI_LengthArrayJson(json);
break;
}
}
}

View File

@ -302,6 +302,8 @@
}
});
grunt.loadNpmTasks("grunt-sass");
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.loadNpmTasks('grunt-text-replace');

View File

@ -12,6 +12,7 @@
"grunt-contrib-uglify": "^0.7.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-karma": "^0.10.1",
"grunt-sass": "^0.18.0",
"grunt-text-replace": "^0.4.0",
"jasmine-core": "^2.2.0",
"karma": "^0.12.31",

View File

@ -29,8 +29,9 @@
#include "conversions.h"
#include "Basics/tri-strings.h"
#include "Basics/fpconv.h"
#include "Basics/string-buffer.h"
#include "Basics/tri-strings.h"
// -----------------------------------------------------------------------------
// --SECTION-- public functions for string to something
@ -715,9 +716,10 @@ char* TRI_StringUInt64 (uint64_t attr) {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief convert to string from double
/// @brief convert to string from double (currently not used)
////////////////////////////////////////////////////////////////////////////////
#if 0
char* TRI_StringDouble (double value) {
TRI_string_buffer_t buffer;
@ -726,6 +728,7 @@ char* TRI_StringDouble (double value) {
return buffer._buffer;
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief convert to hex string from uint32, using the specified buffer.

View File

@ -243,10 +243,12 @@ char* TRI_StringInt64 (int64_t);
char* TRI_StringUInt64 (uint64_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief convert to string from double
/// @brief convert to string from double (currently not used)
////////////////////////////////////////////////////////////////////////////////
#if 0
char* TRI_StringDouble (double);
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief convert to a hex string from uint32, using the specified buffer.

332
lib/Basics/fpconv.cpp Normal file
View File

@ -0,0 +1,332 @@
#include <stdbool.h>
#include <string.h>
#include "fpconv.h"
#include "powers.h"
#define fracmask 0x000FFFFFFFFFFFFFU
#define expmask 0x7FF0000000000000U
#define hiddenbit 0x0010000000000000U
#define signmask 0x8000000000000000U
#define expbias (1023 + 52)
#define absv(n) ((n) < 0 ? -(n) : (n))
#define minv(a, b) ((a) < (b) ? (a) : (b))
static uint64_t tens[] = {
10000000000000000000U, 1000000000000000000U, 100000000000000000U,
10000000000000000U, 1000000000000000U, 100000000000000U,
10000000000000U, 1000000000000U, 100000000000U,
10000000000U, 1000000000U, 100000000U,
10000000U, 1000000U, 100000U,
10000U, 1000U, 100U,
10U, 1U
};
static inline uint64_t get_dbits(double d)
{
union {
double dbl;
uint64_t i;
} dbl_bits = { d };
return dbl_bits.i;
}
static Fp build_fp(double d)
{
uint64_t bits = get_dbits(d);
Fp fp;
fp.frac = bits & fracmask;
fp.exp = (bits & expmask) >> 52;
if(fp.exp) {
fp.frac += hiddenbit;
fp.exp -= expbias;
} else {
fp.exp = -expbias + 1;
}
return fp;
}
static void normalize(Fp* fp)
{
while ((fp->frac & hiddenbit) == 0) {
fp->frac <<= 1;
fp->exp--;
}
int shift = 64 - 52 - 1;
fp->frac <<= shift;
fp->exp -= shift;
}
static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper)
{
upper->frac = (fp->frac << 1) + 1;
upper->exp = fp->exp - 1;
while ((upper->frac & (hiddenbit << 1)) == 0) {
upper->frac <<= 1;
upper->exp--;
}
int u_shift = 64 - 52 - 2;
upper->frac <<= u_shift;
upper->exp = upper->exp - u_shift;
int l_shift = fp->frac == hiddenbit ? 2 : 1;
lower->frac = (fp->frac << l_shift) - 1;
lower->exp = fp->exp - l_shift;
lower->frac <<= lower->exp - upper->exp;
lower->exp = upper->exp;
}
static Fp multiply(Fp* a, Fp* b)
{
const uint64_t lomask = 0x00000000FFFFFFFF;
uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask);
uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32);
uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask);
uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32);
uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32);
/* round up */
tmp += 1U << 31;
Fp fp = {
ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32),
a->exp + b->exp + 64
};
return fp;
}
static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac)
{
while (rem < frac && delta - rem >= kappa &&
(rem + kappa < frac || frac - rem > rem + kappa - frac)) {
digits[ndigits - 1]--;
rem += kappa;
}
}
static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K)
{
uint64_t wfrac = upper->frac - fp->frac;
uint64_t delta = upper->frac - lower->frac;
Fp one;
one.frac = 1ULL << -upper->exp;
one.exp = upper->exp;
uint64_t part1 = upper->frac >> -one.exp;
uint64_t part2 = upper->frac & (one.frac - 1);
int idx = 0, kappa = 10;
uint64_t* divp;
/* 1000000000 */
for(divp = tens + 10; kappa > 0; divp++) {
uint64_t div = *divp;
unsigned digit = part1 / div;
if (digit || idx) {
digits[idx++] = digit + '0';
}
part1 -= digit * div;
kappa--;
uint64_t tmp = (part1 <<-one.exp) + part2;
if (tmp <= delta) {
*K += kappa;
round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac);
return idx;
}
}
/* 10 */
uint64_t* unit = tens + 18;
while(true) {
part2 *= 10;
delta *= 10;
kappa--;
unsigned digit = part2 >> -one.exp;
if (digit || idx) {
digits[idx++] = digit + '0';
}
part2 &= one.frac - 1;
if (part2 < delta) {
*K += kappa;
round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit);
return idx;
}
unit--;
}
}
static int grisu2(double d, char* digits, int* K)
{
Fp w = build_fp(d);
Fp lower, upper;
get_normalized_boundaries(&w, &lower, &upper);
normalize(&w);
int k;
Fp cp = find_cachedpow10(upper.exp, &k);
w = multiply(&w, &cp);
upper = multiply(&upper, &cp);
lower = multiply(&lower, &cp);
lower.frac++;
upper.frac--;
*K = -k;
return generate_digits(&w, &upper, &lower, digits, K);
}
static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg)
{
int exp = absv(K + ndigits - 1);
/* write plain integer */
if(K >= 0 && (exp < (ndigits + 7))) {
memcpy(dest, digits, ndigits);
memset(dest + ndigits, '0', K);
return ndigits + K;
}
/* write decimal w/o scientific notation */
if(K < 0 && (K > -7 || exp < 4)) {
int offset = ndigits - absv(K);
/* fp < 1.0 -> write leading zero */
if(offset <= 0) {
offset = -offset;
dest[0] = '0';
dest[1] = '.';
memset(dest + 2, '0', offset);
memcpy(dest + offset + 2, digits, ndigits);
return ndigits + 2 + offset;
/* fp > 1.0 */
} else {
memcpy(dest, digits, offset);
dest[offset] = '.';
memcpy(dest + offset + 1, digits + offset, ndigits - offset);
return ndigits + 1;
}
}
/* write decimal w/ scientific notation */
ndigits = minv(ndigits, 18 - neg);
int idx = 0;
dest[idx++] = digits[0];
if(ndigits > 1) {
dest[idx++] = '.';
memcpy(dest + idx, digits + 1, ndigits - 1);
idx += ndigits - 1;
}
dest[idx++] = 'e';
char sign = K + ndigits - 1 < 0 ? '-' : '+';
dest[idx++] = sign;
int cent = 0;
if(exp > 99) {
cent = exp / 100;
dest[idx++] = cent + '0';
exp -= cent * 100;
}
if(exp > 9) {
int dec = exp / 10;
dest[idx++] = dec + '0';
exp -= dec * 10;
} else if(cent) {
dest[idx++] = '0';
}
dest[idx++] = exp % 10 + '0';
return idx;
}
static int filter_special(double fp, char* dest)
{
if(fp == 0.0) {
dest[0] = '0';
return 1;
}
uint64_t bits = get_dbits(fp);
bool nan = (bits & expmask) == expmask;
if(!nan) {
return 0;
}
if(bits & fracmask) {
dest[0] = 'N'; dest[1] = 'a'; dest[2] = 'N';
} else {
dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f';
}
return 3;
}
int fpconv_dtoa(double d, char dest[24])
{
char digits[18];
int str_len = 0;
bool neg = false;
if(get_dbits(d) & signmask) {
dest[0] = '-';
str_len++;
neg = true;
}
int spec = filter_special(d, dest + str_len);
if(spec) {
return str_len + spec;
}
int K = 0;
int ndigits = grisu2(d, digits, &K);
str_len += emit_digits(digits, ndigits, dest + str_len, K, neg);
return str_len;
}

33
lib/Basics/fpconv.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef FPCONV_H
#define FPCONV_H
/* Fast and accurate double to string conversion based on Florian Loitsch's
* Grisu-algorithm[1].
*
* Input:
* fp -> the double to convert, dest -> destination buffer.
* The generated string will never be longer than 24 characters.
* Make sure to pass a pointer to at least 24 bytes of memory.
* The emitted string will not be null terminated.
*
* Output:
* The number of written characters.
*
* Exemplary usage:
*
* void print(double d)
* {
* char buf[24 + 1] // plus null terminator
* int str_len = fpconv_dtoa(d, buf);
*
* buf[str_len] = '\0';
* printf("%s", buf);
* }
*
*/
int fpconv_dtoa(double fp, char dest[24]);
#endif
/* [1] http://florian.loitsch.com/publications/dtoa-pldi2010.pdf */

87
lib/Basics/powers.h Normal file
View File

@ -0,0 +1,87 @@
#include <stdint.h>
#define npowers 87
#define steppowers 8
#define firstpower -348 /* 10 ^ -348 */
#define expmax -32
#define expmin -60
typedef struct Fp {
uint64_t frac;
int exp;
} Fp;
static Fp powers_ten[] = {
{ 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 },
{ 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 },
{ 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 },
{ 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 },
{ 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 },
{ 15227053142812498563U, -954 }, { 11345038669416679861U, -927 },
{ 16905424996341287883U, -901 }, { 12595523146049147757U, -874 },
{ 9384396036005875287U, -847 }, { 13983839803942852151U, -821 },
{ 10418772551374772303U, -794 }, { 15525180923007089351U, -768 },
{ 11567161174868858868U, -741 }, { 17236413322193710309U, -715 },
{ 12842128665889583758U, -688 }, { 9568131466127621947U, -661 },
{ 14257626930069360058U, -635 }, { 10622759856335341974U, -608 },
{ 15829145694278690180U, -582 }, { 11793632577567316726U, -555 },
{ 17573882009934360870U, -529 }, { 13093562431584567480U, -502 },
{ 9755464219737475723U, -475 }, { 14536774485912137811U, -449 },
{ 10830740992659433045U, -422 }, { 16139061738043178685U, -396 },
{ 12024538023802026127U, -369 }, { 17917957937422433684U, -343 },
{ 13349918974505688015U, -316 }, { 9946464728195732843U, -289 },
{ 14821387422376473014U, -263 }, { 11042794154864902060U, -236 },
{ 16455045573212060422U, -210 }, { 12259964326927110867U, -183 },
{ 18268770466636286478U, -157 }, { 13611294676837538539U, -130 },
{ 10141204801825835212U, -103 }, { 15111572745182864684U, -77 },
{ 11258999068426240000U, -50 }, { 16777216000000000000U, -24 },
{ 12500000000000000000U, 3 }, { 9313225746154785156U, 30 },
{ 13877787807814456755U, 56 }, { 10339757656912845936U, 83 },
{ 15407439555097886824U, 109 }, { 11479437019748901445U, 136 },
{ 17105694144590052135U, 162 }, { 12744735289059618216U, 189 },
{ 9495567745759798747U, 216 }, { 14149498560666738074U, 242 },
{ 10542197943230523224U, 269 }, { 15709099088952724970U, 295 },
{ 11704190886730495818U, 322 }, { 17440603504673385349U, 348 },
{ 12994262207056124023U, 375 }, { 9681479787123295682U, 402 },
{ 14426529090290212157U, 428 }, { 10748601772107342003U, 455 },
{ 16016664761464807395U, 481 }, { 11933345169920330789U, 508 },
{ 17782069995880619868U, 534 }, { 13248674568444952270U, 561 },
{ 9871031767461413346U, 588 }, { 14708983551653345445U, 614 },
{ 10959046745042015199U, 641 }, { 16330252207878254650U, 667 },
{ 12166986024289022870U, 694 }, { 18130221999122236476U, 720 },
{ 13508068024458167312U, 747 }, { 10064294952495520794U, 774 },
{ 14996968138956309548U, 800 }, { 11173611982879273257U, 827 },
{ 16649979327439178909U, 853 }, { 12405201291620119593U, 880 },
{ 9242595204427927429U, 907 }, { 13772540099066387757U, 933 },
{ 10261342003245940623U, 960 }, { 15290591125556738113U, 986 },
{ 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 },
{ 12648080533535911531U, 1066 }
};
static Fp find_cachedpow10(int exp, int* k)
{
const double one_log_ten = 0.30102999566398114;
int approx = -(exp + npowers) * one_log_ten;
int idx = (approx - firstpower) / steppowers;
while(1) {
int current = exp + powers_ten[idx].exp + 64;
if(current < expmin) {
idx++;
continue;
}
if(current > expmax) {
idx--;
continue;
}
*k = (firstpower + idx * steppowers);
return powers_ten[idx];
}
}

View File

@ -28,8 +28,8 @@
////////////////////////////////////////////////////////////////////////////////
#include "string-buffer.h"
#include <stdlib.h>
#include "Basics/conversions.h"
#include "Basics/fpconv.h"
#include "Zip/zip.h"
// -----------------------------------------------------------------------------
@ -1282,10 +1282,7 @@ int TRI_AppendSizeHexStringBuffer (TRI_string_buffer_t * self, size_t attr) {
////////////////////////////////////////////////////////////////////////////////
int TRI_AppendDoubleStringBuffer (TRI_string_buffer_t * self, double attr) {
// IEEE754 NaN values have an interesting property that we can exploit...
// if the architecture does not use IEEE754 values then this shouldn't do
// any harm either
if (attr != attr) {
if (std::isnan(attr)) {
return TRI_AppendStringStringBuffer(self, "NaN");
}
@ -1296,182 +1293,16 @@ int TRI_AppendDoubleStringBuffer (TRI_string_buffer_t * self, double attr) {
return TRI_AppendStringStringBuffer(self, "-inf");
}
int res = Reserve(self, 1);
int res = Reserve(self, 24);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
if (attr < 0.0) {
AppendChar(self, '-');
attr = -attr;
}
else if (attr == 0.0) {
AppendChar(self, '0');
return TRI_ERROR_NO_ERROR;
}
int length = fpconv_dtoa(attr, self->_current);
self->_current += static_cast<size_t>(length);
if (((double)((uint32_t) attr)) == attr) {
return TRI_AppendUInt32StringBuffer(self, (uint32_t) attr);
}
else if (attr < (double) 429496U) {
uint32_t smll;
smll = (uint32_t)(attr * 10000.0);
if (((double) smll) == attr * 10000.0) {
uint32_t ep;
TRI_AppendUInt32StringBuffer(self, smll / 10000);
ep = smll % 10000;
if (ep != 0) {
size_t pos;
char a1;
char a2;
char a3;
char a4;
pos = 0;
res = Reserve(self, 6);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
AppendChar(self, '.');
if ((ep / 1000L) % 10 != 0) pos = 1;
a1 = (char) ((ep / 1000L) % 10 + '0');
if ((ep / 100L) % 10 != 0) pos = 2;
a2 = (char) ((ep / 100L) % 10 + '0');
if ((ep / 10L) % 10 != 0) pos = 3;
a3 = (char) ((ep / 10L) % 10 + '0');
if (ep % 10 != 0) pos = 4;
a4 = (char) (ep % 10 + '0');
AppendChar(self, a1);
if (pos > 1) { AppendChar(self, a2); }
if (pos > 2) { AppendChar(self, a3); }
if (pos > 3) { AppendChar(self, a4); }
}
return TRI_ERROR_NO_ERROR;
}
}
// we do not habe a small integral number nor small decimal number with only a few decimal digits
// there at most 16 significant digits, first find out if we have an integer value
if (10000000000000000.0 < attr) {
size_t n;
n = 0;
while (10000000000000000.0 < attr) {
attr /= 10.0;
++n;
}
res = TRI_AppendUInt64StringBuffer(self, (uint64_t) attr);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
res = Reserve(self, n);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
for (; 0 < n; --n) {
AppendChar(self, '0');
}
return TRI_ERROR_NO_ERROR;
}
// very small, i. e. less than 1
else if (attr < 1.0) {
size_t n;
n = 0;
while (attr < 1.0) {
attr *= 10.0;
++n;
// should not happen, so it must be almost 0
if (n > 400) {
return TRI_AppendUInt32StringBuffer(self, 0);
}
}
res = Reserve(self, n + 2);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
AppendChar(self, '0');
AppendChar(self, '.');
for (--n; 0 < n; --n) {
AppendChar(self, '0');
}
attr = 10000000000000000.0 * attr;
return TRI_AppendUInt64StringBuffer(self, (uint64_t) attr);
}
// somewhere in between
else {
uint64_t m;
double d;
size_t n;
m = (uint64_t) attr;
d = attr - m;
n = 0;
TRI_AppendUInt64StringBuffer(self, m);
while (d < 1.0) {
d *= 10.0;
++n;
// should not happen, so it must be almost 0
if (n > 400) {
return TRI_ERROR_NO_ERROR;
}
}
res = Reserve(self, n + 1);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
AppendChar(self, '.');
for (--n; 0 < n; --n) {
AppendChar(self, '0');
}
d = 10000000000000000.0 * d;
return TRI_AppendUInt64StringBuffer(self, (uint64_t) d);
}
return TRI_ERROR_NO_ERROR;
}
// -----------------------------------------------------------------------------

View File

@ -58,6 +58,7 @@ add_library(
Basics/fasthash.cpp
Basics/files.cpp
Basics/FileUtils.cpp
Basics/fpconv.cpp
Basics/hashes.cpp
Basics/init.cpp
Basics/InitialiseBasics.cpp

View File

@ -25,6 +25,7 @@ lib_libarango_a_SOURCES = \
lib/Basics/fasthash.cpp \
lib/Basics/files.cpp \
lib/Basics/FileUtils.cpp \
lib/Basics/fpconv.cpp \
lib/Basics/hashes.cpp \
lib/Basics/init.cpp \
lib/Basics/InitialiseBasics.cpp \