Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
R
rapidjson
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
rapidjson
Commits
98dd0a0a
Commit
98dd0a0a
authored
Sep 13, 2014
by
Milo Yip
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make custom strtod work for denormal numbers and some boundary cases [ci skip]
parent
4bd240ab
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
68 additions
and
82 deletions
+68
-82
strtod.h
include/rapidjson/internal/strtod.h
+42
-20
readertest.cpp
test/unittest/readertest.cpp
+26
-62
No files found.
include/rapidjson/internal/strtod.h
View file @
98dd0a0a
...
...
@@ -29,9 +29,13 @@ namespace internal {
class
Double
{
public
:
Double
()
{}
Double
(
double
d
)
:
d
(
d
)
{}
Double
(
uint64_t
u
)
:
u
(
u
)
{}
double
Value
()
const
{
return
d
;
}
uint64_t
Uint64Value
()
const
{
return
u
;
}
double
NextDouble
()
const
{
RAPIDJSON_ASSERT
(
!
Sign
());
return
Double
(
u
+
1
).
Value
();
...
...
@@ -47,12 +51,20 @@ public:
bool
Sign
()
const
{
return
(
u
&
kSignMask
)
!=
0
;
}
uint64_t
Significand
()
const
{
return
u
&
kSignificandMask
;
}
int
Exponent
()
const
{
return
(
u
&
kExponentMask
)
-
kExponentBias
;
}
double
Value
()
const
{
return
d
;}
int
Exponent
()
const
{
return
((
u
&
kExponentMask
)
>>
kSignificandSize
)
-
kExponentBias
;
}
bool
IsNan
()
const
{
return
(
u
&
kExponentMask
)
==
kExponentMask
&&
Significand
()
!=
0
;
}
bool
IsInf
()
const
{
return
(
u
&
kExponentMask
)
==
kExponentMask
&&
Significand
()
==
0
;
}
bool
IsNormal
()
const
{
return
(
u
&
kExponentMask
)
!=
0
||
Significand
()
==
0
;
}
uint64_t
IntegerSignificand
()
const
{
return
IsNormal
()
?
Significand
()
|
kHiddenBit
:
Significand
();
}
int
IntegerExponent
()
const
{
return
(
IsNormal
()
?
Exponent
()
:
kDenormalExponent
)
-
kSignificandSize
;
}
uint64_t
ToBias
()
const
{
return
(
u
&
kSignMask
)
?
~
u
+
1
:
u
|
kSignMask
;
}
private
:
static
const
int
kSignificandSize
=
52
;
static
const
int
kExponentBias
=
0x3FF
;
static
const
int
kDenormalExponent
=
1
-
kExponentBias
;
static
const
uint64_t
kSignMask
=
RAPIDJSON_UINT64_C2
(
0x80000000
,
0x00000000
);
static
const
uint64_t
kExponentMask
=
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
);
static
const
uint64_t
kSignificandMask
=
RAPIDJSON_UINT64_C2
(
0x000FFFFF
,
0xFFFFFFFF
);
...
...
@@ -328,19 +340,10 @@ inline double NormalPrecision(double d, int p) {
}
inline
int
CheckWithinHalfULP
(
double
b
,
const
BigInteger
&
d
,
int
dExp
,
bool
*
adjustToNegative
)
{
static
const
int
kSignificandSize
=
52
;
static
const
int
kExponentBias
=
0x3FF
;
static
const
uint64_t
kExponentMask
=
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
);
static
const
uint64_t
kSignificandMask
=
RAPIDJSON_UINT64_C2
(
0x000FFFFF
,
0xFFFFFFFF
);
static
const
uint64_t
kHiddenBit
=
RAPIDJSON_UINT64_C2
(
0x00100000
,
0x00000000
);
const
Double
db
(
b
);
const
uint64_t
bInt
=
db
.
IntegerSignificand
();
const
int
bExp
=
db
.
IntegerExponent
();
union
{
double
d
;
uint64_t
u
;
}
u
;
u
.
d
=
b
;
const
uint64_t
bInt
=
(
u
.
u
&
kSignificandMask
)
|
kHiddenBit
;
const
int
bExp
=
((
u
.
u
&
kExponentMask
)
>>
kSignificandSize
)
-
kExponentBias
-
kSignificandSize
;
const
int
hExp
=
bExp
-
1
;
int
dS_Exp2
=
0
;
...
...
@@ -396,7 +399,19 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adj
BigInteger
delta
(
0
);
*
adjustToNegative
=
dS
.
Difference
(
bS
,
&
delta
);
return
delta
.
Compare
(
hS
);
int
cmp
=
delta
.
Compare
(
hS
);
// If delta is within 1/2 ULP, check for special case when significand is power of two.
// In this case, need to compare with 1/2h in the lower bound.
if
(
cmp
<
0
&&
*
adjustToNegative
&&
db
.
IsNormal
()
&&
(
bInt
&
(
bInt
-
1
))
==
0
)
{
delta
<<=
1
;
if
(
delta
.
Compare
(
hS
)
<=
0
)
return
-
1
;
else
return
1
;
}
return
cmp
;
}
inline
double
FullPrecision
(
double
d
,
int
dExp
,
const
char
*
decimals
,
size_t
length
)
{
...
...
@@ -413,17 +428,16 @@ inline double FullPrecision(double d, int dExp, const char* decimals, size_t len
}
}
if
(
p
>=
-
22
&&
d
<=
9007199254740991
.
0
)
// 2^53 - 1
if
(
p
>=
-
22
&&
p
<=
22
&&
d
<=
9007199254740991
.
0
)
// 2^53 - 1
return
StrtodFastPath
(
d
,
p
);
if
(
p
+
int
(
length
)
<
-
324
)
return
0
.
0
;
//printf("s=%s p=%d\n", decimals, p);
const
BigInteger
dInt
(
decimals
,
length
);
Double
approx
=
NormalPrecision
(
d
,
p
);
for
(;;)
{
//printf("approx=%.17g\n", approx.Value());
bool
lastAdjustToNegative
;
for
(
int
i
=
0
;
i
<
10
;
i
++
)
{
bool
adjustToNegative
;
int
cmp
=
CheckWithinHalfULP
(
approx
.
Value
(),
dInt
,
dExp
,
&
adjustToNegative
);
if
(
cmp
<
0
)
...
...
@@ -436,13 +450,21 @@ inline double FullPrecision(double d, int dExp, const char* decimals, size_t len
return
approx
.
Value
();
}
else
{
// If oscillate between negative/postive, terminate
if
(
i
>
0
&&
adjustToNegative
!=
lastAdjustToNegative
)
break
;
// adjustment
if
(
adjustToNegative
)
approx
=
approx
.
PreviousDouble
();
else
approx
=
approx
.
NextDouble
();
}
}
lastAdjustToNegative
=
adjustToNegative
;
}
// This should not happen, but in case there is really a bug, break the infinite-loop
return
approx
.
Value
();
}
}
// namespace internal
...
...
test/unittest/readertest.cpp
View file @
98dd0a0a
...
...
@@ -31,36 +31,6 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF
(
effc
++
)
#endif
// Implement functions that may not be provided pre-C++11
bool
IsNan
(
double
x
)
{
union
{
double
x
;
uint64_t
u
;
}
u
=
{
x
};
// E == 0x7FF && M != 0
return
(
u
.
u
&
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
))
==
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
)
&&
(
u
.
u
&
RAPIDJSON_UINT64_C2
(
0x000FFFFF
,
0xFFFFFFFF
))
!=
0
;
}
bool
IsInf
(
double
x
)
{
union
{
double
x
;
uint64_t
u
;
}
u
=
{
x
};
// E = 0x7FF and M == 0
return
(
u
.
u
&
RAPIDJSON_UINT64_C2
(
0x7FFFFFFF
,
0xFFFFFFFF
))
==
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
);
}
bool
IsNormal
(
double
x
)
{
union
{
double
x
;
uint64_t
u
;
}
u
=
{
x
};
// E != 0 || M == 0
return
(
u
.
u
&
RAPIDJSON_UINT64_C2
(
0x7FF00000
,
0x00000000
))
!=
0
||
(
u
.
u
&
RAPIDJSON_UINT64_C2
(
0x000FFFFF
,
0xFFFFFFFF
))
==
0
;
}
template
<
bool
expect
>
struct
ParseBoolHandler
:
BaseReaderHandler
<
UTF8
<>
,
ParseBoolHandler
<
expect
>
>
{
ParseBoolHandler
()
:
step_
(
0
)
{}
...
...
@@ -242,16 +212,22 @@ static void TestParseDouble() {
TEST_DOUBLE
(
fullPrecision
,
"1.234E+10"
,
1.234E+10
);
TEST_DOUBLE
(
fullPrecision
,
"1.234E-10"
,
1.234E-10
);
TEST_DOUBLE
(
fullPrecision
,
"1.79769e+308"
,
1.79769e+308
);
//
TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308);
TEST_DOUBLE
(
fullPrecision
,
"2.22507e-308"
,
2.22507e-308
);
TEST_DOUBLE
(
fullPrecision
,
"-1.79769e+308"
,
-
1.79769e+308
);
//TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308);
//TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal
TEST_DOUBLE
(
fullPrecision
,
"-2.22507e-308"
,
-
2.22507e-308
);
TEST_DOUBLE
(
fullPrecision
,
"4.9406564584124654e-324"
,
4.9406564584124654e-324
);
// minimum denormal
TEST_DOUBLE
(
fullPrecision
,
"2.2250738585072009e-308"
,
2.2250738585072009e-308
);
// Max subnormal double
TEST_DOUBLE
(
fullPrecision
,
"2.2250738585072014e-308"
,
2.2250738585072014e-308
);
// Min normal positive double
TEST_DOUBLE
(
fullPrecision
,
"1.7976931348623157e+308"
,
1.7976931348623157e+308
);
// Max double
TEST_DOUBLE
(
fullPrecision
,
"1e-10000"
,
0.0
);
// must underflow
TEST_DOUBLE
(
fullPrecision
,
"18446744073709551616"
,
18446744073709551616.0
);
// 2^64 (max of uint64_t + 1, force to use double)
TEST_DOUBLE
(
fullPrecision
,
"-9223372036854775809"
,
-
9223372036854775809.0
);
// -2^63 - 1(min of int64_t + 1, force to use double)
TEST_DOUBLE
(
fullPrecision
,
"0.9868011474609375"
,
0.9868011474609375
);
// https://github.com/miloyip/rapidjson/issues/120
TEST_DOUBLE
(
fullPrecision
,
"123e34"
,
123e34
);
// Fast Path Cases In Disguise
TEST_DOUBLE
(
fullPrecision
,
"45913141877270640000.0"
,
45913141877270640000.0
);
TEST_DOUBLE
(
fullPrecision
,
"2.2250738585072011e-308"
,
2.2250738585072011e-308
);
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
TEST_DOUBLE
(
fullPrecision
,
"2.2250738585072012e-308"
,
2.2250738585072012e-308
);
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
TEST_DOUBLE
(
fullPrecision
,
"4503599627370495.9999999999"
,
4503599627370495.9999999999
);
// slightly smaller than 2^52 for power of two case
{
char
n1e308
[
310
];
// '1' followed by 308 '0'
...
...
@@ -263,24 +239,23 @@ static void TestParseDouble() {
}
#if 1
static
const
unsigned
count
=
1000000
;
// Random test for double
{
union
{
double
d
;
uint64_t
u
;
}
u
;
Random
r
;
for
(
unsigned
i
=
0
;
i
<
100000
;
i
++
)
{
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
internal
::
Double
d
;
do
{
// Need to call r() in two statements for cross-platform coherent sequence.
u
.
u
=
uint64_t
(
r
())
<<
32
;
u
.
u
|=
uint64_t
(
r
());
}
while
(
IsNan
(
u
.
d
)
||
IsInf
(
u
.
d
)
||
!
IsNormal
(
u
.
d
));
uint64_t
u
=
uint64_t
(
r
())
<<
32
;
u
|=
uint64_t
(
r
());
d
=
internal
::
Double
(
u
);
}
while
(
d
.
IsNan
()
||
d
.
IsInf
()
/* || !d.IsNormal()*/
);
// Also work for subnormal now
char
buffer
[
32
];
*
internal
::
dtoa
(
u
.
d
,
buffer
)
=
'\0'
;
TEST_DOUBLE
(
fullPrecision
,
buffer
,
u
.
d
);
*
internal
::
dtoa
(
d
.
Value
()
,
buffer
)
=
'\0'
;
TEST_DOUBLE
(
fullPrecision
,
buffer
,
d
.
Value
()
);
}
}
#endif
...
...
@@ -298,32 +273,21 @@ TEST(Reader, ParseNumber_FullPrecisionDouble) {
TEST
(
Reader
,
ParseNumber_NormalPrecisionError
)
{
static
unsigned
count
=
1000000
;
static
const
uint64_t
kSignMask
=
RAPIDJSON_UINT64_C2
(
0x80000000
,
0
);
union
{
double
d
;
uint64_t
u
;
uint64_t
ToBias
()
const
{
if
(
u
&
kSignMask
)
return
~
u
+
1
;
else
return
u
|
kSignMask
;
}
}
u
,
a
;
Random
r
;
double
ulpSum
=
0.0
;
double
ulpMax
=
0.0
;
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
internal
::
Double
e
,
a
;
do
{
// Need to call r() in two statements for cross-platform coherent sequence.
u
.
u
=
uint64_t
(
r
())
<<
32
;
u
.
u
|=
uint64_t
(
r
());
}
while
(
IsNan
(
u
.
d
)
||
IsInf
(
u
.
d
)
||
!
IsNormal
(
u
.
d
));
uint64_t
u
=
uint64_t
(
r
())
<<
32
;
u
|=
uint64_t
(
r
());
e
=
u
;
}
while
(
e
.
IsNan
()
||
e
.
IsInf
()
||
!
e
.
IsNormal
());
char
buffer
[
32
];
*
internal
::
dtoa
(
u
.
d
,
buffer
)
=
'\0'
;
*
internal
::
dtoa
(
e
.
Value
()
,
buffer
)
=
'\0'
;
StringStream
s
(
buffer
);
ParseDoubleHandler
h
;
...
...
@@ -331,8 +295,8 @@ TEST(Reader, ParseNumber_NormalPrecisionError) {
ASSERT_EQ
(
kParseErrorNone
,
reader
.
Parse
(
s
,
h
).
Code
());
EXPECT_EQ
(
1u
,
h
.
step_
);
a
.
d
=
h
.
actual_
;
uint64_t
bias1
=
u
.
ToBias
();
a
=
h
.
actual_
;
uint64_t
bias1
=
e
.
ToBias
();
uint64_t
bias2
=
a
.
ToBias
();
double
ulp
=
bias1
>=
bias2
?
bias1
-
bias2
:
bias2
-
bias1
;
ulpMax
=
std
::
max
(
ulpMax
,
ulp
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment