Skip to content

Commit 22f0a42

Browse files
committed
[MERGE #5905 @wyrichte] Fixes #5906 - Makes NaN respect the sign bit
Merge pull request #5905 from wyrichte:build/wyrichte/NaN_neg_0
2 parents f1a2cdc + eba9b1a commit 22f0a42

13 files changed

+90
-14
lines changed

lib/Common/Common/NumberUtilities.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ using namespace Js;
1717
// Redeclare static constants
1818
const UINT64 NumberConstantsBase::k_Nan;
1919
const UINT32 NumberConstantsBase::k_Nan32;
20+
const UINT64 NumberConstantsBase::k_NegativeNan;
2021
const INT64 NumberUtilitiesBase::Pos_InvalidInt64;
2122
const INT64 NumberUtilitiesBase::Neg_InvalidInt64;
2223
const uint64 NumberConstants::k_PosInf;
@@ -59,6 +60,7 @@ using namespace Js;
5960
const double NumberConstants::MAX_VALUE = *(double*)(&NumberConstants::k_PosMax);
6061
const double NumberConstants::MIN_VALUE = *(double*)(&NumberConstants::k_PosMin);
6162
const double NumberConstants::NaN = *(double*)(&NumberConstants::k_Nan);
63+
const double NumberConstants::NegativeNaN = *(double*)(&NumberConstants::k_NegativeNan);
6264
const double NumberConstants::NEGATIVE_INFINITY= *(double*)(&NumberConstants::k_NegInf);
6365
const double NumberConstants::POSITIVE_INFINITY= *(double*)(&NumberConstants::k_PosInf );
6466
const double NumberConstants::NEG_ZERO= *(double*)(&NumberConstants::k_NegZero );

lib/Common/Common/NumberUtilities.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ namespace Js
6464
static const double MAX_VALUE;
6565
static const double MIN_VALUE;
6666
static const double NaN;
67+
static const double NegativeNaN;
6768
static const double NEGATIVE_INFINITY;
6869
static const double POSITIVE_INFINITY;
6970
static const double NEG_ZERO;
@@ -99,6 +100,7 @@ namespace Js
99100

100101
static bool IsFinite(double value);
101102
static bool IsNan(double value);
103+
static bool IsNegative(double value);
102104
static bool IsFloat32NegZero(float value);
103105
static bool IsSpecial(double value, uint64 nSpecial);
104106
static uint64 ToSpecial(double value);

lib/Common/Common/NumberUtilities.inl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ namespace Js
137137
NUMBER_UTIL_INLINE bool NumberUtilities::IsNan(double value)
138138
{
139139
#if defined(TARGET_64)
140-
// NaN is a range of values; all bits on the exponent are 1's and some nonzero significant.
141-
// no distinction on signed NaN's
140+
// NaN is a range of values; all bits on the exponent are 1's
141+
// and some nonzero significant. No distinction on signed NaN's.
142142
uint64 nCompare = ToSpecial(value);
143143
bool isNan = (0 == (~nCompare & 0x7FF0000000000000ull) &&
144144
0 != (nCompare & 0x000FFFFFFFFFFFFFull));
@@ -149,6 +149,12 @@ namespace Js
149149
#endif
150150
}
151151

152+
NUMBER_UTIL_INLINE bool NumberUtilities::IsNegative(double value)
153+
{
154+
uint64 nCompare = ToSpecial(value);
155+
return nCompare & 0x8000000000000000ull;
156+
}
157+
152158
NUMBER_UTIL_INLINE bool NumberUtilities::IsSpecial(double value, uint64 nSpecial)
153159
{
154160
// Perform a bitwise comparison using uint64 instead of a double comparison, since that

lib/Common/Common/NumberUtilitiesBase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ namespace Js
1212
class NumberConstantsBase
1313
{
1414
public:
15-
static const UINT64 k_Nan = 0xFFF8000000000000ull;
15+
static const UINT64 k_Nan = 0x7FF8000000000000ull;
1616
static const UINT32 k_Nan32 = 0x7FC00000ul;
17+
static const UINT64 k_NegativeNan = 0xFFF8000000000000ull;
1718
};
1819

1920
class NumberUtilitiesBase

lib/Runtime/Language/JavascriptConversion.inl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,11 @@ namespace Js {
297297
#if FLOATVAR
298298
if (typeId == TypeIds_Number)
299299
{
300-
// NaN could have sign bit set, but that isn't observable so canonicalize to positive NaN
301300
double numberValue = JavascriptNumber::GetValue(value);
302301
return JavascriptNumber::IsNan(numberValue)
303-
? JavascriptNumber::ToVar(JavascriptNumber::NaN)
302+
? JavascriptNumber::IsNegative(numberValue)
303+
? JavascriptNumber::ToVar(JavascriptNumber::NegativeNaN)
304+
: JavascriptNumber::ToVar(JavascriptNumber::NaN)
304305
: value;
305306
}
306307
#else

lib/Runtime/Library/JavascriptNumber.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace Js
2323
#if FLOATVAR
2424
if (IsNan(value))
2525
{
26-
value = JavascriptNumber::NaN;
26+
value = IsNegative(value) ? JavascriptNumber::NegativeNaN : JavascriptNumber::NaN;
2727
}
2828
#endif
2929
return JavascriptNumber::NewInlined(value, scriptContext);

lib/Runtime/Library/JavascriptNumber.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ namespace Js
5555
static bool TryToVarFastWithCheck(double value, Var* result);
5656

5757
inline static BOOL IsNan(double value) { return NumberUtilities::IsNan(value); }
58+
inline static BOOL IsNegative(double value) { return NumberUtilities::IsNegative(value); }
5859
static bool IsZero(double value);
5960
static BOOL IsNegZero(double value);
6061
static bool IsPosInf(double value);

lib/Runtime/Library/JavascriptNumber.inl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Js
1414
#endif
1515
)
1616
{
17-
AssertMsg(!IsNan(value) || ToSpecial(value) == k_Nan || ToSpecial(value) == 0x7FF8000000000000ull, "We should only produce a NaN with this value");
17+
AssertMsg(!IsNan(value) || ToSpecial(value) == k_NegativeNan || ToSpecial(value) == 0x7FF8000000000000ull, "We should only produce a NaN with this value");
1818
SetSpecial(ToSpecial(value) ^ FloatTag_Value);
1919
}
2020
#else
@@ -96,7 +96,7 @@ namespace Js
9696
#if FLOATVAR
9797
if (IsNan(value))
9898
{
99-
value = JavascriptNumber::NaN;
99+
value = IsNegative(value) ? JavascriptNumber::NegativeNaN : JavascriptNumber::NaN;
100100
}
101101

102102
*result = JavascriptNumber::ToVar(value);
@@ -128,7 +128,7 @@ namespace Js
128128
{
129129
if (IsNan(value))
130130
{
131-
value = JavascriptNumber::NaN;
131+
value = IsNegative(value) ? JavascriptNumber::NegativeNaN : JavascriptNumber::NaN;
132132
}
133133
return ToVar(value);
134134
}
@@ -148,7 +148,7 @@ namespace Js
148148
inline Var JavascriptNumber::ToVar(double value)
149149
{
150150
uint64 val = *(uint64*)&value;
151-
AssertMsg(!IsNan(value) || ToSpecial(value) == k_Nan || ToSpecial(value) == 0x7FF8000000000000ull, "We should only produce a NaN with this value");
151+
AssertMsg(!IsNan(value) || ToSpecial(value) == k_NegativeNan || ToSpecial(value) == 0x7FF8000000000000ull, "We should only produce a NaN with this value");
152152
return reinterpret_cast<Var>(val ^ FloatTag_Value);
153153
}
154154

test/Number/NegativeNaN.baseline

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
0,0,0,0,0,0,248,127
2+
0,0,0,0,0,0,248,255
3+
0,0,192,127
4+
0,0,192,255
5+
7ff8000000000000
6+
fff8000000000000
7+
0,0,0,0,0,0,248,127
8+
0,0,0,0,0,0,248,255
9+
0,0,192,127
10+
0,0,192,255
11+
7ff8000000000000
12+
fff8000000000000

test/Number/NegativeNaN.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
function f(){
7+
f64 = new Float64Array([NaN]);
8+
u8 = new Uint8Array(f64.buffer);
9+
print(u8)
10+
11+
f64 = new Float64Array([-NaN]);
12+
u8 = new Uint8Array(f64.buffer);
13+
print(u8)
14+
15+
f64 = new Float32Array([NaN]);
16+
u8 = new Uint8Array(f64.buffer);
17+
print(u8)
18+
19+
f64 = new Float32Array([-NaN]);
20+
u8 = new Uint8Array(f64.buffer);
21+
print(u8)
22+
23+
// GitHub bug #398
24+
function numberToRawBits(v) {
25+
var isLittleEndian = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
26+
var reduce = Array.prototype[isLittleEndian ? 'reduceRight' : 'reduce'];
27+
var uint8 = new Uint8Array(new Float64Array([v]).buffer);
28+
return reduce.call(uint8, (a, v) => a + (v < 16 ? "0" : "") + v.toString(16), "");
29+
}
30+
31+
console.log(numberToRawBits(NaN));
32+
console.log(numberToRawBits(-NaN));
33+
34+
}
35+
f()
36+
f()
37+

0 commit comments

Comments
 (0)