Commit c343f309 authored by ahaas's avatar ahaas Committed by Commit bot

[turbofan] Change TruncateFloat64ToUint64 to TryTruncateFloatToUint64.

This operator now provides a second output which indicates whether the conversion from float64 to uint64 was successful or not. The second output returns 0 if the conversion fails, or something else if the conversion succeeds.

The second output can be ignored, which means that the operator can be used the same as the original operator.

I implement the new operator on x64 and arm64. @v8-mips-ports and @v8-ppc-ports, can you please take care of the mips64 and ppc64 implementation of the second output?

R=titzer@chromium.org, v8-arm-ports@googlegroups.com

Review URL: https://codereview.chromium.org/1507703002

Cr-Commit-Position: refs/heads/master@{#32705}
parent 8e771d9b
...@@ -1054,6 +1054,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1054,6 +1054,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break; break;
case kArm64Float64ToUint64: case kArm64Float64ToUint64:
__ Fcvtzu(i.OutputRegister64(), i.InputDoubleRegister(0)); __ Fcvtzu(i.OutputRegister64(), i.InputDoubleRegister(0));
if (i.OutputCount() > 1) {
__ Fcmp(i.InputDoubleRegister(0), 0.0);
__ Ccmp(i.OutputRegister(0), -1, ZFlag, ge);
__ Cset(i.OutputRegister(1), ne);
}
break; break;
case kArm64Int32ToFloat64: case kArm64Int32ToFloat64:
__ Scvtf(i.OutputDoubleRegister(), i.InputRegister32(0)); __ Scvtf(i.OutputDoubleRegister(), i.InputRegister32(0));
......
...@@ -1264,8 +1264,20 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) { ...@@ -1264,8 +1264,20 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) {
} }
void InstructionSelector::VisitTruncateFloat64ToUint64(Node* node) { void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
VisitRR(this, kArm64Float64ToUint64, node); Arm64OperandGenerator g(this);
InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
InstructionOperand outputs[2];
size_t output_count = 0;
outputs[output_count++] = g.DefineAsRegister(node);
Node* success_output = NodeProperties::FindProjection(node, 1);
if (success_output) {
outputs[output_count++] = g.DefineAsRegister(success_output);
}
Emit(kArm64Float64ToUint64, output_count, outputs, 1, inputs);
} }
......
...@@ -832,8 +832,8 @@ void InstructionSelector::VisitNode(Node* node) { ...@@ -832,8 +832,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsWord64(node), VisitTryTruncateFloat64ToInt64(node); return MarkAsWord64(node), VisitTryTruncateFloat64ToInt64(node);
case IrOpcode::kTruncateFloat32ToUint64: case IrOpcode::kTruncateFloat32ToUint64:
return MarkAsWord64(node), VisitTruncateFloat32ToUint64(node); return MarkAsWord64(node), VisitTruncateFloat32ToUint64(node);
case IrOpcode::kTruncateFloat64ToUint64: case IrOpcode::kTryTruncateFloat64ToUint64:
return MarkAsWord64(node), VisitTruncateFloat64ToUint64(node); return MarkAsWord64(node), VisitTryTruncateFloat64ToUint64(node);
case IrOpcode::kChangeInt32ToInt64: case IrOpcode::kChangeInt32ToInt64:
return MarkAsWord64(node), VisitChangeInt32ToInt64(node); return MarkAsWord64(node), VisitChangeInt32ToInt64(node);
case IrOpcode::kChangeUint32ToUint64: case IrOpcode::kChangeUint32ToUint64:
...@@ -1097,7 +1097,7 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) { ...@@ -1097,7 +1097,7 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) {
} }
void InstructionSelector::VisitTruncateFloat64ToUint64(Node* node) { void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
...@@ -1209,6 +1209,7 @@ void InstructionSelector::VisitProjection(Node* node) { ...@@ -1209,6 +1209,7 @@ void InstructionSelector::VisitProjection(Node* node) {
case IrOpcode::kInt32AddWithOverflow: case IrOpcode::kInt32AddWithOverflow:
case IrOpcode::kInt32SubWithOverflow: case IrOpcode::kInt32SubWithOverflow:
case IrOpcode::kTryTruncateFloat64ToInt64: case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTryTruncateFloat64ToUint64:
if (ProjectionIndexOf(node->op()) == 0u) { if (ProjectionIndexOf(node->op()) == 0u) {
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value)); Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
} else { } else {
......
...@@ -138,7 +138,7 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) { ...@@ -138,7 +138,7 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) {
V(TruncateFloat32ToInt64, Operator::kNoProperties, 1, 0, 1) \ V(TruncateFloat32ToInt64, Operator::kNoProperties, 1, 0, 1) \
V(TryTruncateFloat64ToInt64, Operator::kNoProperties, 1, 0, 2) \ V(TryTruncateFloat64ToInt64, Operator::kNoProperties, 1, 0, 2) \
V(TruncateFloat32ToUint64, Operator::kNoProperties, 1, 0, 1) \ V(TruncateFloat32ToUint64, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat64ToUint64, Operator::kNoProperties, 1, 0, 1) \ V(TryTruncateFloat64ToUint64, Operator::kNoProperties, 1, 0, 2) \
V(ChangeInt32ToFloat64, Operator::kNoProperties, 1, 0, 1) \ V(ChangeInt32ToFloat64, Operator::kNoProperties, 1, 0, 1) \
V(RoundInt64ToFloat32, Operator::kNoProperties, 1, 0, 1) \ V(RoundInt64ToFloat32, Operator::kNoProperties, 1, 0, 1) \
V(RoundInt64ToFloat64, Operator::kNoProperties, 1, 0, 1) \ V(RoundInt64ToFloat64, Operator::kNoProperties, 1, 0, 1) \
......
...@@ -210,7 +210,7 @@ class MachineOperatorBuilder final : public ZoneObject { ...@@ -210,7 +210,7 @@ class MachineOperatorBuilder final : public ZoneObject {
const Operator* TruncateFloat32ToInt64(); const Operator* TruncateFloat32ToInt64();
const Operator* TryTruncateFloat64ToInt64(); const Operator* TryTruncateFloat64ToInt64();
const Operator* TruncateFloat32ToUint64(); const Operator* TruncateFloat32ToUint64();
const Operator* TruncateFloat64ToUint64(); const Operator* TryTruncateFloat64ToUint64();
const Operator* ChangeInt32ToFloat64(); const Operator* ChangeInt32ToFloat64();
const Operator* ChangeInt32ToInt64(); const Operator* ChangeInt32ToInt64();
const Operator* ChangeUint32ToFloat64(); const Operator* ChangeUint32ToFloat64();
......
...@@ -1126,8 +1126,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1126,8 +1126,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
case kMips64TruncUlD: { case kMips64TruncUlD: {
FPURegister scratch = kScratchDoubleReg; FPURegister scratch = kScratchDoubleReg;
Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
// TODO(plind): Fix wrong param order of Trunc_ul_d() macro-asm function. // TODO(plind): Fix wrong param order of Trunc_ul_d() macro-asm function.
__ Trunc_ul_d(i.InputDoubleRegister(0), i.OutputRegister(), scratch); __ Trunc_ul_d(i.InputDoubleRegister(0), i.OutputRegister(0), scratch,
result);
break; break;
} }
case kMips64BitcastDL: case kMips64BitcastDL:
......
...@@ -876,8 +876,20 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) { ...@@ -876,8 +876,20 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) {
} }
void InstructionSelector::VisitTruncateFloat64ToUint64(Node* node) { void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
VisitRR(this, kMips64TruncUlD, node); Mips64OperandGenerator g(this);
InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
InstructionOperand outputs[2];
size_t output_count = 0;
outputs[output_count++] = g.DefineAsRegister(node);
Node* success_output = NodeProperties::FindProjection(node, 1);
if (success_output) {
outputs[output_count++] = g.DefineAsRegister(success_output);
}
Emit(kMips64TruncUlD, output_count, outputs, 1, inputs);
} }
......
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
V(TruncateFloat32ToInt64) \ V(TruncateFloat32ToInt64) \
V(TryTruncateFloat64ToInt64) \ V(TryTruncateFloat64ToInt64) \
V(TruncateFloat32ToUint64) \ V(TruncateFloat32ToUint64) \
V(TruncateFloat64ToUint64) \ V(TryTruncateFloat64ToUint64) \
V(ChangeInt32ToFloat64) \ V(ChangeInt32ToFloat64) \
V(ChangeInt32ToInt64) \ V(ChangeInt32ToInt64) \
V(ChangeUint32ToFloat64) \ V(ChangeUint32ToFloat64) \
......
...@@ -946,7 +946,11 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) { ...@@ -946,7 +946,11 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) {
} }
void InstructionSelector::VisitTruncateFloat64ToUint64(Node* node) { void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
if (NodeProperties::FindProjection(node, 1)) {
// TODO(ppc): implement the second return value.
UNIMPLEMENTED();
}
VisitRR(this, kPPC_DoubleToUint64, node); VisitRR(this, kPPC_DoubleToUint64, node);
} }
......
...@@ -442,7 +442,12 @@ class RawMachineAssembler { ...@@ -442,7 +442,12 @@ class RawMachineAssembler {
return AddNode(machine()->TruncateFloat32ToUint64(), a); return AddNode(machine()->TruncateFloat32ToUint64(), a);
} }
Node* TruncateFloat64ToUint64(Node* a) { Node* TruncateFloat64ToUint64(Node* a) {
return AddNode(machine()->TruncateFloat64ToUint64(), a); // TODO(ahaas): Remove this function as soon as it is not used anymore in
// WebAssembly.
return AddNode(machine()->TryTruncateFloat64ToUint64(), a);
}
Node* TryTruncateFloat64ToUint64(Node* a) {
return AddNode(machine()->TryTruncateFloat64ToUint64(), a);
} }
Node* ChangeInt32ToInt64(Node* a) { Node* ChangeInt32ToInt64(Node* a) {
return AddNode(machine()->ChangeInt32ToInt64(), a); return AddNode(machine()->ChangeInt32ToInt64(), a);
......
...@@ -2136,7 +2136,7 @@ Type* Typer::Visitor::TypeTruncateFloat32ToUint64(Node* node) { ...@@ -2136,7 +2136,7 @@ Type* Typer::Visitor::TypeTruncateFloat32ToUint64(Node* node) {
} }
Type* Typer::Visitor::TypeTruncateFloat64ToUint64(Node* node) { Type* Typer::Visitor::TypeTryTruncateFloat64ToUint64(Node* node) {
return Type::Internal(); return Type::Internal();
} }
......
...@@ -920,7 +920,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -920,7 +920,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kTruncateFloat32ToInt64: case IrOpcode::kTruncateFloat32ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64: case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTruncateFloat32ToUint64: case IrOpcode::kTruncateFloat32ToUint64:
case IrOpcode::kTruncateFloat64ToUint64: case IrOpcode::kTryTruncateFloat64ToUint64:
case IrOpcode::kFloat64ExtractLowWord32: case IrOpcode::kFloat64ExtractLowWord32:
case IrOpcode::kFloat64ExtractHighWord32: case IrOpcode::kFloat64ExtractHighWord32:
case IrOpcode::kFloat64InsertLowWord32: case IrOpcode::kFloat64InsertLowWord32:
......
...@@ -1106,11 +1106,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1106,11 +1106,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} else { } else {
__ Cvttsd2siq(i.OutputRegister(), i.InputOperand(0)); __ Cvttsd2siq(i.OutputRegister(), i.InputOperand(0));
} }
if (instr->OutputCount() > 1) {
__ Set(i.OutputRegister(1), 0);
}
// Check if the result of the Float64ToInt64 conversion is positive, we // Check if the result of the Float64ToInt64 conversion is positive, we
// are already done. // are already done.
__ testq(i.OutputRegister(), i.OutputRegister()); __ testq(i.OutputRegister(), i.OutputRegister());
Label done; Label done;
__ j(positive, &done); Label success;
__ j(positive, &success);
// The result of the first conversion was negative, which means that the // The result of the first conversion was negative, which means that the
// input value was not within the positive int64 range. We subtract 2^64 // input value was not within the positive int64 range. We subtract 2^64
// and convert it again to see if it is within the uint64 range. // and convert it again to see if it is within the uint64 range.
...@@ -1128,9 +1132,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1128,9 +1132,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
// The input value is within uint64 range and the second conversion worked // The input value is within uint64 range and the second conversion worked
// successfully, but we still have to undo the subtraction we did // successfully, but we still have to undo the subtraction we did
// earlier. // earlier.
__ movq(kScratchRegister, Immediate(1)); __ Set(kScratchRegister, 0x8000000000000000);
__ shlq(kScratchRegister, Immediate(63));
__ orq(i.OutputRegister(), kScratchRegister); __ orq(i.OutputRegister(), kScratchRegister);
__ bind(&success);
if (instr->OutputCount() > 1) {
__ Set(i.OutputRegister(1), 1);
}
__ bind(&done); __ bind(&done);
break; break;
} }
......
...@@ -862,9 +862,19 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) { ...@@ -862,9 +862,19 @@ void InstructionSelector::VisitTruncateFloat32ToUint64(Node* node) {
} }
void InstructionSelector::VisitTruncateFloat64ToUint64(Node* node) { void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) {
X64OperandGenerator g(this); X64OperandGenerator g(this);
Emit(kSSEFloat64ToUint64, g.DefineAsRegister(node), g.Use(node->InputAt(0))); InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};
InstructionOperand outputs[2];
size_t output_count = 0;
outputs[output_count++] = g.DefineAsRegister(node);
Node* success_output = NodeProperties::FindProjection(node, 1);
if (success_output) {
outputs[output_count++] = g.DefineAsRegister(success_output);
}
Emit(kSSEFloat64ToUint64, output_count, outputs, 1, inputs);
} }
...@@ -1464,8 +1474,6 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch, ...@@ -1464,8 +1474,6 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
case IrOpcode::kInt32SubWithOverflow: case IrOpcode::kInt32SubWithOverflow:
cont.OverwriteAndNegateIfEqual(kOverflow); cont.OverwriteAndNegateIfEqual(kOverflow);
return VisitBinop(this, node, kX64Sub32, &cont); return VisitBinop(this, node, kX64Sub32, &cont);
case IrOpcode::kTryTruncateFloat64ToInt64:
return VisitTryTruncateFloat64ToInt64(result);
default: default:
break; break;
} }
......
...@@ -1631,8 +1631,8 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd, ...@@ -1631,8 +1631,8 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd,
} }
void MacroAssembler::Trunc_ul_d(FPURegister fd, FPURegister fs, void MacroAssembler::Trunc_ul_d(FPURegister fd, FPURegister fs,
FPURegister scratch) { FPURegister scratch, Register result) {
Trunc_ul_d(fs, t8, scratch); Trunc_ul_d(fs, t8, scratch, result);
dmtc1(t8, fd); dmtc1(t8, fd);
} }
...@@ -1698,9 +1698,14 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd, ...@@ -1698,9 +1698,14 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd,
void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs, void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs,
FPURegister scratch) { FPURegister scratch, Register result) {
DCHECK(!fd.is(scratch)); DCHECK(!fd.is(scratch));
DCHECK(!rs.is(at)); DCHECK(!AreAliased(rs, result, at));
if (result.is_valid()) {
mov(result, zero_reg);
Move(kDoubleRegZero, 0.0);
}
// Load 2^63 into scratch as its double representation. // Load 2^63 into scratch as its double representation.
li(at, 0x43e0000000000000); li(at, 0x43e0000000000000);
...@@ -1708,8 +1713,9 @@ void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs, ...@@ -1708,8 +1713,9 @@ void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs,
// Test if scratch > fd. // Test if scratch > fd.
// If fd < 2^63 we can convert it normally. // If fd < 2^63 we can convert it normally.
Label simple_convert, done; // If fd is unordered the conversion fails.
BranchF(&simple_convert, NULL, lt, fd, scratch); Label simple_convert, done, fail;
BranchF(&simple_convert, &fail, lt, fd, scratch);
// First we subtract 2^63 from fd, then trunc it to rs // First we subtract 2^63 from fd, then trunc it to rs
// and add 2^63 to rs. // and add 2^63 to rs.
...@@ -1725,6 +1731,13 @@ void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs, ...@@ -1725,6 +1731,13 @@ void MacroAssembler::Trunc_ul_d(FPURegister fd, Register rs,
dmfc1(rs, scratch); dmfc1(rs, scratch);
bind(&done); bind(&done);
if (result.is_valid()) {
// Conversion is failed if the result is negative or unordered.
BranchF(&fail, &fail, lt, scratch, kDoubleRegZero);
li(result, Operand(1));
}
bind(&fail);
} }
......
...@@ -837,8 +837,10 @@ class MacroAssembler: public Assembler { ...@@ -837,8 +837,10 @@ class MacroAssembler: public Assembler {
void Trunc_uw_d(FPURegister fd, Register rs, FPURegister scratch); void Trunc_uw_d(FPURegister fd, Register rs, FPURegister scratch);
// Convert double to unsigned long. // Convert double to unsigned long.
void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch); void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch,
void Trunc_ul_d(FPURegister fd, Register rs, FPURegister scratch); Register result = no_reg);
void Trunc_ul_d(FPURegister fd, Register rs, FPURegister scratch,
Register result = no_reg);
// Convert single to unsigned long. // Convert single to unsigned long.
void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch); void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch);
......
...@@ -5427,7 +5427,7 @@ TEST(RunTryTruncateFloat64ToInt64WithCheck) { ...@@ -5427,7 +5427,7 @@ TEST(RunTryTruncateFloat64ToInt64WithCheck) {
FOR_FLOAT64_INPUTS(i) { FOR_FLOAT64_INPUTS(i) {
if (*i < 9223372036854775808.0 && *i > -9223372036854775809.0) { if (*i < 9223372036854775808.0 && *i > -9223372036854775809.0) {
// Conversions within this range // Conversions within this range should succeed.
CHECK_EQ(static_cast<int64_t>(*i), m.Call(*i)); CHECK_EQ(static_cast<int64_t>(*i), m.Call(*i));
CHECK_NE(0, success); CHECK_NE(0, success);
} else { } else {
...@@ -5456,7 +5456,7 @@ TEST(RunTruncateFloat32ToUint64) { ...@@ -5456,7 +5456,7 @@ TEST(RunTruncateFloat32ToUint64) {
} }
TEST(RunTruncateFloat64ToUint64) { TEST(RunTryTruncateFloat64ToUint64WithoutCheck) {
BufferedRawMachineAssemblerTester<uint64_t> m(kMachFloat64); BufferedRawMachineAssemblerTester<uint64_t> m(kMachFloat64);
m.Return(m.TruncateFloat64ToUint64(m.Parameter(0))); m.Return(m.TruncateFloat64ToUint64(m.Parameter(0)));
...@@ -5467,10 +5467,26 @@ TEST(RunTruncateFloat64ToUint64) { ...@@ -5467,10 +5467,26 @@ TEST(RunTruncateFloat64ToUint64) {
CHECK_EQ(static_cast<uint64_t>(input), m.Call(input)); CHECK_EQ(static_cast<uint64_t>(input), m.Call(input));
} }
} }
}
TEST(RunTryTruncateFloat64ToUint64WithCheck) {
int64_t success = 0;
BufferedRawMachineAssemblerTester<int64_t> m(kMachFloat64);
Node* trunc = m.TryTruncateFloat64ToUint64(m.Parameter(0));
Node* val = m.Projection(0, trunc);
Node* check = m.Projection(1, trunc);
m.StoreToPointer(&success, kMachInt64, check);
m.Return(val);
FOR_FLOAT64_INPUTS(i) { FOR_FLOAT64_INPUTS(i) {
if (*i < 18446744073709551616.0 && *i >= 0) { if (*i < 18446744073709551616.0 && *i >= 0) {
// Conversions within this range should succeed.
CHECK_EQ(static_cast<uint64_t>(*i), m.Call(*i)); CHECK_EQ(static_cast<uint64_t>(*i), m.Call(*i));
CHECK_NE(0, success);
} else {
m.Call(*i);
CHECK_EQ(0, success);
} }
} }
} }
......
...@@ -99,12 +99,39 @@ class ValueHelper { ...@@ -99,12 +99,39 @@ class ValueHelper {
static std::vector<double> float64_vector() { static std::vector<double> float64_vector() {
static const double nan = std::numeric_limits<double>::quiet_NaN(); static const double nan = std::numeric_limits<double>::quiet_NaN();
static const double values[] = { static const double values[] = {0.125,
0.125, 0.25, 0.375, 0.5, 1.25, -1.75, 2, 5.125, 6.25, 0.0, -0.0, 0.25,
982983.25, 888, 2147483647.0, -999.75, 3.1e7, -2e66, 3e-88, 0.375,
-2147483648.0, V8_INFINITY, -V8_INFINITY, -nan, nan, 2147483647.375, 0.5,
2147483647.75, 2147483648.0, 2147483648.25, 2147483649.25, 1.25,
-2147483647.0, -2147483647.125, -2147483647.875, -2147483648.25, -1.75,
2,
5.125,
6.25,
0.0,
-0.0,
982983.25,
888,
2147483647.0,
-999.75,
3.1e7,
-2e66,
2e66,
3e-88,
-2147483648.0,
V8_INFINITY,
-V8_INFINITY,
-nan,
nan,
2147483647.375,
2147483647.75,
2147483648.0,
2147483648.25,
2147483649.25,
-2147483647.0,
-2147483647.125,
-2147483647.875,
-2147483648.25,
-2147483649.5}; -2147483649.5};
return std::vector<double>(&values[0], &values[arraysize(values)]); return std::vector<double>(&values[0], &values[arraysize(values)]);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment