diff --git a/runtime/compiler/codegen/J9CodeGenerator.hpp b/runtime/compiler/codegen/J9CodeGenerator.hpp
index 0a514012216..3fa06e6cd6f 100644
--- a/runtime/compiler/codegen/J9CodeGenerator.hpp
+++ b/runtime/compiler/codegen/J9CodeGenerator.hpp
@@ -532,6 +532,16 @@ void addMonClass(TR::Node* monNode, TR_OpaqueClassBlock* clazz);
*/
void setSupportsInlineVectorizedHashCode() { _j9Flags.set(SupportsInlineVectorizedHashCode); }
+ /** \brief
+ * Determines whether the code generator supports inlining of java_lang_Math_max/min_F/D
+ */
+ bool getSupportsInlineMath_MaxMin_FD() { return _j9Flags.testAny(SupportsInlineMath_MaxMin_FD); }
+
+ /** \brief
+ * The code generator supports inlining of java_lang_Math_max/min_F/D
+ */
+ void setSupportsInlineMath_MaxMin_FD() { _j9Flags.set(SupportsInlineMath_MaxMin_FD); }
+
/**
* \brief
* The number of nodes between a monext and the next monent before
@@ -699,6 +709,7 @@ void addMonClass(TR::Node* monNode, TR_OpaqueClassBlock* clazz);
SupportsInlineVectorizedHashCode = 0x00002000,
SupportsInlineStringCodingHasNegatives = 0x00004000,
SupportsInlineStringCodingCountPositives = 0x00008000,
+ SupportsInlineMath_MaxMin_FD = 0x00010000,
};
flags32_t _j9Flags;
diff --git a/runtime/compiler/env/j9method.cpp b/runtime/compiler/env/j9method.cpp
index e9a247dc3db..a4b429c5829 100644
--- a/runtime/compiler/env/j9method.cpp
+++ b/runtime/compiler/env/j9method.cpp
@@ -5068,6 +5068,10 @@ TR_ResolvedJ9Method::setRecognizedMethodInfo(TR::RecognizedMethod rm)
case TR::java_lang_Math_min_I:
case TR::java_lang_Math_max_L:
case TR::java_lang_Math_min_L:
+ case TR::java_lang_Math_max_F:
+ case TR::java_lang_Math_min_F:
+ case TR::java_lang_Math_max_D:
+ case TR::java_lang_Math_min_D:
case TR::java_lang_Math_abs_I:
case TR::java_lang_Math_abs_L:
case TR::java_lang_Math_abs_F:
diff --git a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp
index 46f557a8aa0..9c35736609c 100644
--- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp
+++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp
@@ -1435,6 +1435,11 @@ bool J9::RecognizedCallTransformer::isInlineable(TR::TreeTop* treetop)
case TR::java_lang_Math_max_L:
case TR::java_lang_Math_min_L:
return !comp()->getOption(TR_DisableMaxMinOptimization);
+ case TR::java_lang_Math_max_F:
+ case TR::java_lang_Math_min_F:
+ case TR::java_lang_Math_max_D:
+ case TR::java_lang_Math_min_D:
+ return !comp()->getOption(TR_DisableMaxMinOptimization) && cg()->getSupportsInlineMath_MaxMin_FD();
case TR::java_lang_Math_multiplyHigh:
return cg()->getSupportsLMulHigh();
case TR::java_lang_StringUTF16_toBytes:
@@ -1563,6 +1568,18 @@ void J9::RecognizedCallTransformer::transform(TR::TreeTop* treetop)
case TR::java_lang_Math_min_L:
processIntrinsicFunction(treetop, node, TR::lmin);
break;
+ case TR::java_lang_Math_max_F:
+ processIntrinsicFunction(treetop, node, TR::fmax);
+ break;
+ case TR::java_lang_Math_min_F:
+ processIntrinsicFunction(treetop, node, TR::fmin);
+ break;
+ case TR::java_lang_Math_max_D:
+ processIntrinsicFunction(treetop, node, TR::dmax);
+ break;
+ case TR::java_lang_Math_min_D:
+ processIntrinsicFunction(treetop, node, TR::dmin);
+ break;
case TR::java_lang_Math_multiplyHigh:
processIntrinsicFunction(treetop, node, TR::lmulh);
break;
diff --git a/runtime/compiler/z/codegen/J9CodeGenerator.cpp b/runtime/compiler/z/codegen/J9CodeGenerator.cpp
index 8fb71450f0e..7163461ab64 100644
--- a/runtime/compiler/z/codegen/J9CodeGenerator.cpp
+++ b/runtime/compiler/z/codegen/J9CodeGenerator.cpp
@@ -146,6 +146,12 @@ J9::Z::CodeGenerator::initialize()
cg->setSupportsInlineEncodeASCII();
}
+ static bool disableInlineMath_MaxMin_FD = feGetEnv("TR_disableInlineMaxMin") != NULL;
+ if (!disableInlineMath_MaxMin_FD)
+ {
+ cg->setSupportsInlineMath_MaxMin_FD();
+ }
+
static bool disableInlineVectorizedMismatch = feGetEnv("TR_disableInlineVectorizedMismatch") != NULL;
if (cg->getSupportsArrayCmpLen() &&
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
@@ -4154,20 +4160,24 @@ J9::Z::CodeGenerator::inlineDirectCall(
}
}
- if (!comp->getOption(TR_DisableSIMDDoubleMaxMin) && cg->getSupportsVectorRegisters())
- {
- switch (methodSymbol->getRecognizedMethod())
- {
+ if (!self()->comp()->getOption(TR_DisableMaxMinOptimization) && cg->getSupportsInlineMath_MaxMin_FD()) {
+ switch (methodSymbol->getRecognizedMethod()) {
case TR::java_lang_Math_max_D:
- resultReg = TR::TreeEvaluator::inlineDoubleMax(node, cg);
+ resultReg = J9::Z::TreeEvaluator::dmaxEvaluator(node, cg);
return true;
case TR::java_lang_Math_min_D:
- resultReg = TR::TreeEvaluator::inlineDoubleMin(node, cg);
+ resultReg = J9::Z::TreeEvaluator::dminEvaluator(node, cg);
+ return true;
+ case TR::java_lang_Math_max_F:
+ resultReg = J9::Z::TreeEvaluator::fmaxEvaluator(node, cg);
+ return true;
+ case TR::java_lang_Math_min_F:
+ resultReg = J9::Z::TreeEvaluator::fminEvaluator(node, cg);
return true;
default:
break;
- }
}
+ }
switch (methodSymbol->getRecognizedMethod())
{
diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp
index faabbcca960..f21983d278a 100644
--- a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp
+++ b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp
@@ -1066,76 +1066,28 @@ allocateWriteBarrierInternalPointerRegister(TR::CodeGenerator * cg, TR::Node * s
}
-extern TR::Register *
-doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp)
+TR::Register *
+J9::Z::TreeEvaluator::dmaxEvaluator(TR::Node * node, TR::CodeGenerator * cg)
{
- TR_ASSERT(node->getNumChildren() >= 1 || node->getNumChildren() <= 2, "node has incorrect number of children");
-
- /* ===================== Allocating Registers ===================== */
-
- TR::Register * v16 = cg->allocateRegister(TR_VRF);
- TR::Register * v17 = cg->allocateRegister(TR_VRF);
- TR::Register * v18 = cg->allocateRegister(TR_VRF);
-
- /* ===================== Generating instructions ===================== */
-
- /* ====== LD FPR0,16(GPR5) Load a ====== */
- TR::Register * v0 = cg->fprClobberEvaluate(node->getFirstChild());
-
- /* ====== LD FPR2, 0(GPR5) Load b ====== */
- TR::Register * v2 = cg->evaluate(node->getSecondChild());
-
- /* ====== WFTCIDB V16,V0,X'F' a == NaN ====== */
- generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v16, v0, 0xF, 8, 3);
-
- /* ====== For Max: WFCHE V17,V0,V2 Compare a >= b ====== */
- if(isMaxOp)
- {
- generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v0, v2, 0, 8, 3);
- }
- /* ====== For Min: WFCHE V17,V0,V2 Compare a <= b ====== */
- else
- {
- generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v2, v0, 0, 8, 3);
- }
-
- /* ====== VO V16,V16,V17 (a >= b) || (a == NaN) ====== */
- generateVRRcInstruction(cg, TR::InstOpCode::VO, node, v16, v16, v17, 0, 0, 0);
-
- /* ====== For Max: WFTCIDB V17,V0,X'800' a == +0 ====== */
- if(isMaxOp)
- {
- generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x800, 8, 3);
- }
- /* ====== For Min: WFTCIDB V17,V0,X'400' a == -0 ====== */
- else
- {
- generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x400, 8, 3);
- }
- /* ====== WFTCIDB V18,V2,X'C00' b == 0 ====== */
- generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v18, v2, 0xC00, 8, 3);
-
- /* ====== VN V17,V17,V18 (a == -0) && (b == 0) ====== */
- generateVRRcInstruction(cg, TR::InstOpCode::VN, node, v17, v17, v18, 0, 0, 0);
-
- /* ====== VO V16,V16,V17 (a >= b) || (a == NaN) || ((a == -0) && (b == 0)) ====== */
- generateVRRcInstruction(cg, TR::InstOpCode::VO, node, v16, v16, v17, 0, 0, 0);
-
- /* ====== VSEL V0,V0,V2,V16 ====== */
- generateVRReInstruction(cg, TR::InstOpCode::VSEL, node, v0, v0, v2, v16);
-
- /* ===================== Deallocating Registers ===================== */
- cg->stopUsingRegister(v2);
- cg->stopUsingRegister(v16);
- cg->stopUsingRegister(v17);
- cg->stopUsingRegister(v18);
+ return OMR::Z::TreeEvaluator::dmaxHelper(node, cg);
+ }
- node->setRegister(v0);
+TR::Register *
+J9::Z::TreeEvaluator::dminEvaluator(TR::Node * node, TR::CodeGenerator * cg)
+ {
+ return OMR::Z::TreeEvaluator::dminHelper(node, cg);
+ }
- cg->decReferenceCount(node->getFirstChild());
- cg->decReferenceCount(node->getSecondChild());
+TR::Register *
+J9::Z::TreeEvaluator::fmaxEvaluator(TR::Node * node, TR::CodeGenerator * cg)
+ {
+ return OMR::Z::TreeEvaluator::fmaxHelper(node, cg);
+ }
- return node->getRegister();
+TR::Register *
+J9::Z::TreeEvaluator::fminEvaluator(TR::Node * node, TR::CodeGenerator * cg)
+ {
+ return OMR::Z::TreeEvaluator::fminHelper(node, cg);
}
TR::Register*
@@ -2750,19 +2702,7 @@ J9::Z::TreeEvaluator::toLowerIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bo
return caseConversionHelper(node, cg, false, isCompressedString);
}
-TR::Register*
-J9::Z::TreeEvaluator::inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg)
- {
- cg->generateDebugCounter("z13/simd/doubleMax", 1, TR::DebugCounter::Free);
- return doubleMaxMinHelper(node, cg, true);
- }
-TR::Register*
-J9::Z::TreeEvaluator::inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg)
- {
- cg->generateDebugCounter("z13/simd/doubleMin", 1, TR::DebugCounter::Free);
- return doubleMaxMinHelper(node, cg, false);
- }
TR::Register *
J9::Z::TreeEvaluator::inlineMathFma(TR::Node *node, TR::CodeGenerator *cg)
diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.hpp b/runtime/compiler/z/codegen/J9TreeEvaluator.hpp
index 4adf0414fe5..766ff557aaf 100644
--- a/runtime/compiler/z/codegen/J9TreeEvaluator.hpp
+++ b/runtime/compiler/z/codegen/J9TreeEvaluator.hpp
@@ -127,8 +127,10 @@ class OMR_EXTENSIBLE TreeEvaluator: public J9::TreeEvaluator
*/
static TR::Register *inlineVectorizedStringIndexOf(TR::Node *node, TR::CodeGenerator *cg, bool isCompressed);
static TR::Register *inlineIntrinsicIndexOf(TR::Node *node, TR::CodeGenerator *cg, bool isLatin1);
- static TR::Register *inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg);
- static TR::Register *inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg);
+ static TR::Register *fminEvaluator(TR::Node *node, TR::CodeGenerator *cg);
+ static TR::Register *dminEvaluator(TR::Node *node, TR::CodeGenerator *cg);
+ static TR::Register *fmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg);
+ static TR::Register *dmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg);
static TR::Register *inlineMathFma(TR::Node *node, TR::CodeGenerator *cg);
/* This Evaluator generates the SIMD routine for methods
diff --git a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java
index adea8099ce5..cb41265f745 100644
--- a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java
+++ b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java
@@ -23,7 +23,16 @@
package jit.test.recognizedMethod;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;
+import java.util.Random;
+import org.testng.asserts.SoftAssert;
+import static jit.test.recognizedMethod.TestMathUtils.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Listeners;
+import org.testng.AssertJUnit;
+
+
+@Test(singleThreaded=true)
public class TestJavaLangMath {
/**
@@ -39,10 +48,10 @@ public class TestJavaLangMath {
*
* Subsequent tree simplification passes will attempt to reduce this constant
* operation to a dsqrt
IL by performing the square root at compile
- * time. The transformation will be performed when the function get executed
+ * time. The transformation will be performed when the function get executed
* twice, therefore, the "invocationCount=2" is needed. However we must ensure the
- * result of the square root done by the compiler at compile time will be exactly
- * the same as the result had it been done by the Java runtime at runtime. This
+ * result of the square root done by the compiler at compile time will be exactly
+ * the same as the result had it been done by the Java runtime at runtime. This
* test validates the results are the same.
*/
@Test(groups = {"level.sanity"}, invocationCount=2)
@@ -55,4 +64,82 @@ public void test_java_lang_Math_sqrt() {
AssertJUnit.assertEquals(Double.POSITIVE_INFINITY, Math.sqrt(Double.POSITIVE_INFINITY));
AssertJUnit.assertTrue(Double.isNaN(Math.sqrt(Double.NaN)));
}
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="zeroProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_min_zeros_FD(Number a, Number b, boolean isFirstArg) {
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ assertEquals(Math.min(f1, f2), isFirstArg ? f1 : f2);
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ assertEquals(Math.min(f1, f2), isFirstArg ? f1 : f2);
+ }
+ }
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="zeroProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_max_zeros_FD(Number a, Number b, boolean isFirstArg) {
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ assertEquals(Math.max(f1, f2), isFirstArg ? f2 : f1);
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ assertEquals(Math.max(f1, f2), isFirstArg ? f2 : f1);
+ }
+ }
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="nanProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_min_nan_FD(Number a, Number b, Number expected) {
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ AssertJUnit.assertTrue(Float.isNaN(Math.min(f1, f2)));
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ AssertJUnit.assertTrue(Double.isNaN(Math.min(f1, f2)));
+ }
+ }
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="nanProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_max_nan_FD(Number a, Number b, Number expected) {
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ AssertJUnit.assertTrue(Float.isNaN(Math.max(f1, f2)));
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ AssertJUnit.assertTrue(Double.isNaN(Math.max(f1, f2)));
+ }
+ }
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="normalNumberProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_min_normal_FD(Number a, Number b){
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ AssertJUnit.assertEquals(Math.min(f1, f2), f1 <= f2 ? f1 : f2);
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ AssertJUnit.assertEquals(Math.min(f1, f2), f1 <= f2 ? f1 : f2);
+ }
+ }
+
+ @Test(groups = {"level.sanity"}, invocationCount=2, dataProvider="normalNumberProviderFD", dataProviderClass=TestMathUtils.class)
+ public void test_java_lang_Math_max_normal_FD(Number a, Number b){
+ if (a instanceof Float) {
+ float f1 = a.floatValue();
+ float f2 = b.floatValue();
+ AssertJUnit.assertEquals(Math.max(f1, f2), f1 >= f2 ? f1 : f2);
+ } else {
+ double f1 = a.doubleValue();
+ double f2 = b.doubleValue();
+ AssertJUnit.assertEquals(Math.max(f1, f2), f1 >= f2 ? f1 : f2);
+ }
+ }
}
diff --git a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java
new file mode 100644
index 00000000000..3be0ce7d9b1
--- /dev/null
+++ b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright IBM Corp. and others 2019
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which accompanies this
+ * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
+ * or the Apache License, Version 2.0 which accompanies this distribution and
+ * is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * This Source Code may also be made available under the following
+ * Secondary Licenses when the conditions for such availability set
+ * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
+ * General Public License, version 2 with the GNU Classpath
+ * Exception [1] and GNU General Public License, version 2 with the
+ * OpenJDK Assembly Exception [2].
+ *
+ * [1] https://www.gnu.org/software/classpath/license.html
+ * [2] https://openjdk.org/legal/assembly-exception.html
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
+ */
+
+package jit.test.recognizedMethod;
+import java.util.Random;
+import org.testng.asserts.SoftAssert;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.testng.annotations.DataProvider;
+import org.testng.AssertJUnit;
+
+public class TestMathUtils {
+ // constants used to various min/max tests
+ private static final int fqNaNBits = 0x7fcabdef;
+ private static final float fqNaN = Float.intBitsToFloat(fqNaNBits);
+ private static final int fsNaNBits = 0x7faedbaf;
+ private static final float fsNaN = Float.intBitsToFloat(fsNaNBits);
+ private static final int fnZeroBits = 0x80000000;
+ private static final float fpInf = Float.POSITIVE_INFINITY;
+ private static final float fnInf = Float.NEGATIVE_INFINITY;
+ private static final int fpInfBits = Float.floatToRawIntBits(fpInf);
+ private static final int fnInfBits = Float.floatToRawIntBits(fnInf);
+ private static final int fquietBit = 0x00400000;
+ private static final int fNaNExpStart = 0x7f800000;
+ private static final int fNaNMantisaMax = 0x00400000;
+
+ private static final long dqNaNBits = 0x7ff800000000000fL;
+ private static final double dqNaN = Double.longBitsToDouble(dqNaNBits);
+ private static final long dsNaNBits = 0x7ff400000000000fL;
+ private static final double dsNaN = Double.longBitsToDouble(dsNaNBits);
+ private static final long dnZeroBits = 0x8000000000000000L;
+ private static final double dpInf = Double.POSITIVE_INFINITY;
+ private static final double dnInf = Double.NEGATIVE_INFINITY;
+ private static final long dpInfBits = Double.doubleToRawLongBits(dpInf);
+ private static final long dnInfBits = Double.doubleToRawLongBits(dnInf);
+ private static final long dNaNMantisaMax = 0x0008000000000000L;
+ private static final long dNaNExpStart = 0x7ff0000000000000L;
+ private static final long dquietBit = 0x0008000000000000L;
+ private static final Random r = new Random();
+
+ public static class NaNTestPair {
+ T first;
+ T second;
+ BitType bFirst;
+ BitType bSecond;
+ BitType expected;
+ }
+
+ @DataProvider(name="zeroProviderFD")
+ public static Object[][] zeroProviderFD(){
+ return new Object[][]{
+ // arg1, arg2, expected
+ {+0.0f, +0.0f, true},
+ {-0.0f, -0.0f, true},
+ {+0.0f, -0.0f, false},
+ {-0.0f, +0.0f, true},
+
+ {+0.0f, fpInf, true},
+ {fpInf, +0.0f, false},
+ {+0.0f, fnInf, false},
+ {fnInf, +0.0f, true},
+ {-0.0f, fpInf, true},
+ {fpInf, -0.0f, false},
+ {-0.0f, fnInf, false},
+ {fnInf, -0.0f, true},
+
+ {+0.0d, +0.0d, true},
+ {-0.0d, -0.0d, true},
+ {+0.0d, -0.0d, false},
+ {-0.0d, +0.0d, true},
+
+ {+0.0d, dpInf, true},
+ {dpInf, +0.0d, false},
+ {+0.0d, dnInf, false},
+ {dnInf, +0.0d, true},
+ {-0.0d, dpInf, true},
+ {dpInf, -0.0d, false},
+ {-0.0d, dnInf, false},
+ {dnInf, -0.0d, true}
+ };
+ }
+
+ @DataProvider(name="nanProviderFD")
+ public static Object[][] nanProviderFD(){
+ Object[][] constNanPairs = new Object[][] {
+ {fqNaN, fqNaN, fqNaNBits},
+ {fsNaN, fsNaN, fsNaNBits},
+ {fqNaN, fsNaN, fqNaNBits},
+ {fsNaN, fqNaN, fsNaNBits},
+
+ {+0.0f, fqNaN, fqNaNBits},
+ {fqNaN, +0.0f, fqNaNBits},
+ {+0.0f, fsNaN, fsNaNBits},
+ {fsNaN, +0.0f, fsNaNBits},
+ {-0.0f, fqNaN, fqNaNBits},
+ {fqNaN, -0.0f, fqNaNBits},
+ {-0.0f, fsNaN, fsNaNBits},
+ {fsNaN, -0.0f, fsNaNBits},
+
+ {dqNaN, dqNaN, dqNaNBits},
+ {dsNaN, dsNaN, dsNaNBits},
+ {dqNaN, dsNaN, dqNaNBits},
+ {dsNaN, dqNaN, dsNaNBits},
+
+ {+0.0d, dqNaN, dqNaNBits},
+ {dqNaN, +0.0d, dqNaNBits},
+ {+0.0d, dsNaN, dsNaNBits},
+ {dsNaN, +0.0d, dsNaNBits},
+ {-0.0d, dqNaN, dqNaNBits},
+ {dqNaN, -0.0d, dqNaNBits},
+ {-0.0d, dsNaN, dsNaNBits},
+ {dsNaN, -0.0d, dsNaNBits}
+ };
+
+ Object[][] nanPairs = new Object[60][3];
+ int i = 0;
+ while (i < constNanPairs.length) {
+ nanPairs[i] = constNanPairs[i];
+ i++;
+ }
+ while (i < nanPairs.length) {
+ NaNTestPair fpair = getFloatNaNPair();
+ NaNTestPair dpair = getDoubleNaNPair();
+ if (fpair == null) {
+ continue;
+ }
+ nanPairs[i][0] = fpair.first;
+ nanPairs[i][1] = fpair.second;
+ nanPairs[i][2] = fpair.expected;
+ i++;
+ if (dpair == null | i >= nanPairs.length){
+ continue;
+ }
+ nanPairs[i][0] = dpair.first;
+ nanPairs[i][1] = dpair.second;
+ nanPairs[i][2] = dpair.expected;
+ i++;
+ }
+ return nanPairs;
+ }
+
+ @DataProvider(name="normalNumberProviderFD")
+ public static Object[][] normalNumberProviderFD() {
+ Object [][] numList = new Object[20][2];
+ for (int i = 0; i < numList.length / 2; i++) {
+ float a = r.nextFloat() * 1000000.0f;
+ float b = r.nextFloat() * 1000000.0f;
+ int sign1 = r.nextInt(2) == 0 ? -1 : 1;
+ int sign2 = r.nextInt(2) == 0 ? -1 : 1;
+ numList[i][0] = a * sign1;
+ numList[i][1] = b * sign2;
+ }
+ for (int i = numList.length / 2; i < 20; i++) {
+ double a = r.nextDouble() * 1000000000.0d;
+ double b = r.nextDouble() * 1000000000.0d;
+ int sign1 = r.nextInt(2) == 0 ? -1 : 1;
+ int sign2 = r.nextInt(2) == 0 ? -1 : 1;
+ numList[i][0] = a * sign1;
+ numList[i][1] = b * sign2;
+ }
+ return numList;
+ }
+
+ private static NaNTestPair getDoubleNaNPair(){
+ NaNTestPair p = new NaNTestPair();
+ double farg1, farg2;
+ farg1 = farg2 = 0.0f;
+
+ long iarg1;
+ long min = 1L << 13;
+ do {
+ iarg1 = r.nextLong();
+ } while (iarg1 < min);
+ // shift right so that mantisa is non-zero
+ // shift of 13 instead of 12 ensures that the quiet bit is not set
+ iarg1 = iarg1 >> 13;
+
+ int arg1Type = r.nextInt(3); // 0 -> quiet, 1 -> signalling
+ if (arg1Type == 0) {
+ iarg1 = iarg1 | dNaNExpStart | dquietBit;
+ farg1 = Double.longBitsToDouble(iarg1);
+ } else if (arg1Type == 1) {
+ iarg1 = iarg1 | dNaNExpStart;
+ farg1 = Double.longBitsToDouble(iarg1);
+ } else {
+ // Normal number (non NaN)
+ farg1 = r.nextDouble() * 1000000.0 + 1.0;
+ }
+
+ // arg 2
+ long iarg2;
+ do {
+ iarg2 = r.nextLong();
+ iarg2 = iarg2 >> 13;
+ } while (iarg2 < min);
+
+ int arg2Type = r.nextInt(3);
+ if (arg2Type == 0) {
+ iarg2 = iarg2 | dNaNExpStart | dquietBit;
+ farg2 = Double.longBitsToDouble(iarg2);
+ }
+ else if (arg2Type == 1) {
+ iarg2 = iarg2 | dNaNExpStart;
+ farg2 = Double.longBitsToDouble(iarg2);
+ } else {
+ if (arg1Type != 2) {
+ farg2 = r.nextDouble() * 1000000.0 + 1;
+ iarg2 = Double.doubleToRawLongBits(farg2);
+ } else {
+ return null;
+ }
+ }
+
+ p.first = farg1;
+ p.bFirst = iarg1;
+ p.second = farg2;
+ p.bSecond = iarg2;
+ p.expected = (arg1Type != 2 ? iarg1 : iarg2);
+ return p;
+ }
+
+ private static NaNTestPair getFloatNaNPair(){ // 0: max, 1: min
+ NaNTestPair p = new NaNTestPair();
+ float farg1, farg2;
+ farg1 = farg2 = 0.0f;
+
+ int iarg1 = r.nextInt(fNaNMantisaMax - 1) + 1;
+ int arg1Type = r.nextInt(3); // 0 -> quiet, 1 -> signalling
+ if (arg1Type == 0) {
+ iarg1 = iarg1 | fNaNExpStart | fquietBit;
+ farg1 = Float.intBitsToFloat(iarg1);
+ } else if (arg1Type == 1) {
+ iarg1 = iarg1 | fNaNExpStart;
+ farg1 = Float.intBitsToFloat(iarg1);
+ } else {
+ // Normal number (non NaN)
+ farg1 = r.nextFloat() * 1000000.0f + 1.0f;
+ }
+
+ // arg 2
+ int arg2Type = r.nextInt(3);
+ int iarg2 = r.nextInt(fNaNMantisaMax - 1) + 1;
+ if (arg2Type == 0) {
+ iarg2 = (r.nextInt(fNaNMantisaMax - 1) + 1) | fNaNExpStart | fquietBit;
+ farg2 = Float.intBitsToFloat(iarg2);
+ } else if (arg2Type == 1){
+ iarg2 = (r.nextInt(fNaNMantisaMax - 1) + 1) | fNaNExpStart;
+ farg2 = Float.intBitsToFloat(iarg2);
+ } else {
+ if (arg1Type != 2) {
+ farg2 = r.nextFloat() * 1000000.0f + 1f;
+ iarg2 = Float.floatToRawIntBits(farg2);
+ } else {
+ return null;
+ }
+ }
+
+ p.first = farg1;
+ p.second = farg2;
+ p.bFirst = iarg1;
+ p.bSecond = iarg2;
+ p.expected = (arg1Type != 2 ? iarg1 : iarg2);
+ return p;
+ }
+
+ public static void assertEquals(float actual, float expected){
+ AssertJUnit.assertEquals(Float.floatToRawIntBits(expected), Float.floatToRawIntBits(actual));
+ }
+
+ public static void assertEquals(double actual, double expected){
+ AssertJUnit.assertEquals(Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(actual));
+ }
+}