# HG changeset patch # User dchuyko # Date 1599134083 -10800 # Thu Sep 03 14:54:43 2020 +0300 # Node ID 99f3ee7d30c87d4021c84d6f44bd0b51a1c6a001 # Parent d2953de976abf1a3ccb83c6122bdd3b26b4ee6b2 8251525: AARCH64: Faster Math.signum(fp) Reviewed-by: aph, vlivanov, adinn diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -12644,6 +12644,77 @@ ins_pipe(fp_div_d); %} +instruct copySignD_reg(vRegD dst, vRegD src1, vRegD src2, vRegD zero) %{ + match(Set dst (CopySignD src1 (Binary src2 zero))); + effect(TEMP_DEF dst, USE src1, USE src2, USE zero); + format %{ "CopySignD $dst $src1 $src2" %} + ins_encode %{ + FloatRegister dst = as_FloatRegister($dst$$reg), + src1 = as_FloatRegister($src1$$reg), + src2 = as_FloatRegister($src2$$reg), + zero = as_FloatRegister($zero$$reg); + __ fnegd(dst, zero); + __ bsl(dst, __ T8B, src2, src1); + %} + ins_pipe(fp_uop_d); +%} + +instruct copySignF_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (CopySignF src1 src2)); + effect(TEMP_DEF dst, USE src1, USE src2); + format %{ "CopySignF $dst $src1 $src2" %} + ins_encode %{ + FloatRegister dst = as_FloatRegister($dst$$reg), + src1 = as_FloatRegister($src1$$reg), + src2 = as_FloatRegister($src2$$reg); + __ movi(dst, __ T2S, 0x80, 24); + __ bsl(dst, __ T8B, src2, src1); + %} + ins_pipe(fp_uop_d); +%} + +instruct signumD_reg(vRegD dst, vRegD src, vRegD zero, vRegD one) %{ + match(Set dst (SignumD src (Binary zero one))); + effect(TEMP_DEF dst, USE src, USE zero, USE one); + format %{ "signumD $dst, $src" %} + ins_encode %{ + FloatRegister src = as_FloatRegister($src$$reg), + dst = as_FloatRegister($dst$$reg), + zero = as_FloatRegister($zero$$reg), + one = as_FloatRegister($one$$reg); + __ facgtd(dst, src, zero); // dst=0 for +-0.0 and NaN. 0xFFF..F otherwise + __ ushrd(dst, dst, 1); // dst=0 for +-0.0 and NaN. 0x7FF..F otherwise + // Bit selection instruction gets bit from "one" for each enabled bit in + // "dst", otherwise gets a bit from "src". For "src" that contains +-0.0 or + // NaN the whole "src" will be copied because "dst" is zero. For all other + // "src" values dst is 0x7FF..F, which means only the sign bit is copied + // from "src", and all other bits are copied from 1.0. + __ bsl(dst, __ T8B, one, src); + %} + ins_pipe(fp_uop_d); +%} + +instruct signumF_reg(vRegF dst, vRegF src, vRegF zero, vRegF one) %{ + match(Set dst (SignumF src (Binary zero one))); + effect(TEMP_DEF dst, USE src, USE zero, USE one); + format %{ "signumF $dst, $src" %} + ins_encode %{ + FloatRegister src = as_FloatRegister($src$$reg), + dst = as_FloatRegister($dst$$reg), + zero = as_FloatRegister($zero$$reg), + one = as_FloatRegister($one$$reg); + __ facgts(dst, src, zero); // dst=0 for +-0.0 and NaN. 0xFFF..F otherwise + __ ushr(dst, __ T2S, dst, 1); // dst=0 for +-0.0 and NaN. 0x7FF..F otherwise + // Bit selection instruction gets bit from "one" for each enabled bit in + // "dst", otherwise gets a bit from "src". For "src" that contains +-0.0 or + // NaN the whole "src" will be copied because "dst" is zero. For all other + // "src" values dst is 0x7FF..F, which means only the sign bit is copied + // from "src", and all other bits are copied from 1.0. + __ bsl(dst, __ T8B, one, src); + %} + ins_pipe(fp_uop_d); +%} + // ============================================================================ // Logical Instructions diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -1997,6 +1997,21 @@ #undef INSN #undef INSN1 +// Floating-point compare. 3-registers versions (scalar). +#define INSN(NAME, sz, e) \ + void NAME(FloatRegister Vd, FloatRegister Vn, FloatRegister Vm) { \ + starti; \ + f(0b01111110, 31, 24), f(e, 23), f(sz, 22), f(1, 21), rf(Vm, 16); \ + f(0b111011, 15, 10), rf(Vn, 5), rf(Vd, 0); \ + } \ + + INSN(facged, 1, 0); // facge-double + INSN(facges, 0, 0); // facge-single + INSN(facgtd, 1, 1); // facgt-double + INSN(facgts, 0, 1); // facgt-single + +#undef INSN + // Floating-point Move (immediate) private: unsigned pack(double value); @@ -2452,6 +2467,20 @@ #undef INSN +#define INSN(NAME, opc, opc2, isSHR) \ + void NAME(FloatRegister Vd, FloatRegister Vn, int shift){ \ + starti; \ + int encodedShift = isSHR ? 128 - shift : 64 + shift; \ + f(0b01, 31, 30), f(opc, 29), f(0b111110, 28, 23), \ + f(encodedShift, 22, 16); f(opc2, 15, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(shld, 0, 0b010101, /* isSHR = */ false); + INSN(sshrd, 0, 0b000001, /* isSHR = */ true); + INSN(ushrd, 1, 0b000001, /* isSHR = */ true); + +#undef INSN + private: void _ushll(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, int shift) { starti; diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -245,6 +245,12 @@ } } + if (_cpu == CPU_ARM) { + if (FLAG_IS_DEFAULT(UseSignumIntrinsic)) { + FLAG_SET_DEFAULT(UseSignumIntrinsic, true); + } + } + if (_cpu == CPU_ARM && (_model == 0xd07 || _model2 == 0xd07)) _features |= CPU_STXR_PREFETCH; // If an olde style /proc/cpuinfo (cpu_lines == 1) then if _model is an A57 (0xd07) // we assume the worst and assume we could be on a big little system and have diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp --- a/src/hotspot/share/classfile/vmSymbols.cpp +++ b/src/hotspot/share/classfile/vmSymbols.cpp @@ -857,6 +857,14 @@ case vmIntrinsics::_isWhitespace: if (!UseCharacterCompareIntrinsics) return true; break; + case vmIntrinsics::_dcopySign: + case vmIntrinsics::_fcopySign: + if (!InlineMathNatives || !UseCopySignIntrinsic) return true; + break; + case vmIntrinsics::_dsignum: + case vmIntrinsics::_fsignum: + if (!InlineMathNatives || !UseSignumIntrinsic) return true; + break; #endif // COMPILER2 default: return false; diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -769,6 +769,8 @@ do_name(negateExact_name,"negateExact") \ do_name(subtractExact_name,"subtractExact") \ do_name(fma_name, "fma") \ + do_name(copySign_name, "copySign") \ + do_name(signum_name,"signum") \ \ do_intrinsic(_dabs, java_lang_Math, abs_name, double_double_signature, F_S) \ do_intrinsic(_fabs, java_lang_Math, abs_name, float_float_signature, F_S) \ @@ -804,6 +806,10 @@ do_intrinsic(_minF, java_lang_Math, min_name, float2_float_signature, F_S) \ do_intrinsic(_maxD, java_lang_Math, max_name, double2_double_signature, F_S) \ do_intrinsic(_minD, java_lang_Math, min_name, double2_double_signature, F_S) \ + do_intrinsic(_dcopySign, java_lang_Math, copySign_name, double2_double_signature, F_S) \ + do_intrinsic(_fcopySign, java_lang_Math, copySign_name, float2_float_signature, F_S) \ + do_intrinsic(_dsignum, java_lang_Math, signum_name, double_double_signature, F_S) \ + do_intrinsic(_fsignum, java_lang_Math, signum_name, float_float_signature, F_S) \ \ do_intrinsic(_floatToRawIntBits, java_lang_Float, floatToRawIntBits_name, float_int_signature, F_S) \ do_name( floatToRawIntBits_name, "floatToRawIntBits") \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -453,6 +453,18 @@ case vmIntrinsics::_minD: if (!Matcher::match_rule_supported(Op_MinD)) return false; break; + case vmIntrinsics::_dcopySign: + if (!Matcher::match_rule_supported(Op_CopySignD)) return false; + break; + case vmIntrinsics::_fcopySign: + if (!Matcher::match_rule_supported(Op_CopySignF)) return false; + break; + case vmIntrinsics::_dsignum: + if (!Matcher::match_rule_supported(Op_SignumD)) return false; + break; + case vmIntrinsics::_fsignum: + if (!Matcher::match_rule_supported(Op_SignumF)) return false; + break; case vmIntrinsics::_hashCode: case vmIntrinsics::_identityHashCode: case vmIntrinsics::_getClass: diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -283,6 +283,10 @@ macro(ShenandoahLoadReferenceBarrier) #endif macro(SCMemProj) +macro(CopySignD) +macro(CopySignF) +macro(SignumD) +macro(SignumF) macro(SqrtD) macro(SqrtF) macro(Start) diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,3 +98,17 @@ return bottom_type(); } +//------------------------------CopySign----------------------------------------- +CopySignDNode* CopySignDNode::make(PhaseGVN& gvn, Node* in1, Node* in2) { + return new CopySignDNode(in1, in2, gvn.makecon(TypeD::ZERO)); +} + +//------------------------------Signum------------------------------------------- +SignumDNode* SignumDNode::make(PhaseGVN& gvn, Node* in) { + return new SignumDNode(in, gvn.makecon(TypeD::ZERO), gvn.makecon(TypeD::ONE)); +} + +SignumFNode* SignumFNode::make(PhaseGVN& gvn, Node* in) { + return new SignumFNode(in, gvn.makecon(TypeF::ZERO), gvn.makecon(TypeF::ONE)); +} + diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "opto/node.hpp" #include "opto/opcodes.hpp" +#include "opto/connode.hpp" //----------------------PartialSubtypeCheckNode-------------------------------- @@ -216,4 +217,44 @@ virtual uint ideal_reg() const { return Op_RegI; } }; +//------------------------------CopySign----------------------------------------- +class CopySignDNode : public Node { + protected: + CopySignDNode(Node* in1, Node* in2, Node* in3) : Node(0, in1, in2, in3) {} + public: + static CopySignDNode* make(PhaseGVN& gvn, Node* in1, Node* in2); + virtual int Opcode() const; + const Type* bottom_type() const { return TypeLong::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +class CopySignFNode : public Node { + public: + CopySignFNode(Node* in1, Node* in2) : Node(0, in1, in2) {} + virtual int Opcode() const; + const Type* bottom_type() const { return TypeLong::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------Signum------------------------------------------- +class SignumDNode : public Node { + protected: + SignumDNode(Node* in1, Node* in2, Node* in3) : Node(0, in1, in2, in3) {} + public: + static SignumDNode* make(PhaseGVN& gvn, Node* in); + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +class SignumFNode : public Node { + protected: + SignumFNode(Node* in1, Node* in2, Node* in3) : Node(0, in1, in2, in3) {} + public: + static SignumFNode* make(PhaseGVN& gvn, Node* in); + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + #endif // SHARE_VM_OPTO_INTRINSICNODE_HPP diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -544,7 +544,11 @@ case vmIntrinsics::_dexp: case vmIntrinsics::_dlog: case vmIntrinsics::_dlog10: - case vmIntrinsics::_dpow: return inline_math_native(intrinsic_id()); + case vmIntrinsics::_dpow: + case vmIntrinsics::_dcopySign: + case vmIntrinsics::_fcopySign: + case vmIntrinsics::_dsignum: + case vmIntrinsics::_fsignum: return inline_math_native(intrinsic_id()); case vmIntrinsics::_min: case vmIntrinsics::_max: return inline_min_max(intrinsic_id()); @@ -1793,6 +1797,8 @@ switch (id) { case vmIntrinsics::_dabs: n = new AbsDNode( arg); break; case vmIntrinsics::_dsqrt: n = new SqrtDNode(C, control(), arg); break; + case vmIntrinsics::_dcopySign: n = CopySignDNode::make(_gvn, arg, round_double_node(argument(2))); break; + case vmIntrinsics::_dsignum: n = SignumDNode::make(_gvn, arg); break; default: fatal_unexpected_iid(id); break; } set_result(_gvn.transform(n)); @@ -1810,6 +1816,8 @@ case vmIntrinsics::_fabs: n = new AbsFNode( arg); break; case vmIntrinsics::_iabs: n = new AbsINode( arg); break; case vmIntrinsics::_labs: n = new AbsLNode( arg); break; + case vmIntrinsics::_fcopySign: n = new CopySignFNode(arg, argument(1)); break; + case vmIntrinsics::_fsignum: n = SignumFNode::make(_gvn, arg); break; default: fatal_unexpected_iid(id); break; } set_result(_gvn.transform(n)); @@ -1891,6 +1899,11 @@ } #undef FN_PTR + case vmIntrinsics::_dcopySign: return inline_double_math(id); + case vmIntrinsics::_fcopySign: return inline_math(id); + case vmIntrinsics::_dsignum: return inline_double_math(id); + case vmIntrinsics::_fsignum: return inline_math(id); + // These intrinsics are not yet correctly implemented case vmIntrinsics::_datan2: return false; diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -2360,6 +2360,14 @@ n->del_req(3); break; } + case Op_CopySignD: + case Op_SignumF: + case Op_SignumD: { + Node* pair = new BinaryNode(n->in(2), n->in(3)); + n->set_req(2, pair); + n->del_req(3); + break; + } default: break; } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -471,6 +471,12 @@ diagnostic(bool, UseVectorizedMismatchIntrinsic, false, \ "Enables intrinsification of ArraysSupport.vectorizedMismatch()") \ \ + diagnostic(bool, UseCopySignIntrinsic, false, \ + "Enables intrinsification of Math.copySign") \ + \ + diagnostic(bool, UseSignumIntrinsic, false, \ + "Enables intrinsification of Math.signum") \ + \ diagnostic(ccstrlist, DisableIntrinsic, "", \ "do not expand intrinsics whose (internal) names appear here") \ \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1890,6 +1890,10 @@ declare_c2_type(OverflowMulLNode, OverflowLNode) \ declare_c2_type(FmaDNode, Node) \ declare_c2_type(FmaFNode, Node) \ + declare_c2_type(CopySignDNode, Node) \ + declare_c2_type(CopySignFNode, Node) \ + declare_c2_type(SignumDNode, Node) \ + declare_c2_type(SignumFNode, Node) \ \ /*********************/ \ /* Adapter Blob Entries */ \ diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1890,6 +1890,7 @@ * @author Joseph D. Darcy * @since 1.5 */ + @HotSpotIntrinsicCandidate public static double signum(double d) { return (d == 0.0 || Double.isNaN(d))?d:copySign(1.0, d); } @@ -1911,6 +1912,7 @@ * @author Joseph D. Darcy * @since 1.5 */ + @HotSpotIntrinsicCandidate public static float signum(float f) { return (f == 0.0f || Float.isNaN(f))?f:copySign(1.0f, f); } @@ -2131,6 +2133,7 @@ * and the sign of {@code sign}. * @since 1.6 */ + @HotSpotIntrinsicCandidate public static double copySign(double magnitude, double sign) { return Double.longBitsToDouble((Double.doubleToRawLongBits(sign) & (DoubleConsts.SIGN_BIT_MASK)) | @@ -2154,6 +2157,7 @@ * and the sign of {@code sign}. * @since 1.6 */ + @HotSpotIntrinsicCandidate public static float copySign(float magnitude, float sign) { return Float.intBitsToFloat((Float.floatToRawIntBits(sign) & (FloatConsts.SIGN_BIT_MASK)) | diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/TestSignumIntrinsic.java b/test/hotspot/jtreg/compiler/intrinsics/math/TestSignumIntrinsic.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/math/TestSignumIntrinsic.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, BELLSOFT. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test compiler intrinsics for signum + * @requires os.arch=="aarch64" + * @library /test/lib + * + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+UseSignumIntrinsic + * compiler.intrinsics.math.TestSignumIntrinsic + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:-UseSignumIntrinsic -XX:+UseCopySignIntrinsic + * compiler.intrinsics.math.TestSignumIntrinsic + */ + +package compiler.intrinsics.math; + +import jdk.test.lib.Asserts; + +public class TestSignumIntrinsic { + + private static final float[][] float_cases = { + {123.4f, 1.0f}, + {-56.7f, -1.0f}, + {7e30f, 1.0f}, + {-0.3e30f, -1.0f}, + {Float.MAX_VALUE, 1.0f}, + {-Float.MAX_VALUE, -1.0f}, + {Float.MIN_VALUE, 1.0f}, + {-Float.MIN_VALUE, -1.0f}, + {0.0f, 0.0f}, + {-0.0f, -0.0f}, + {Float.POSITIVE_INFINITY, 1.0f}, + {Float.NEGATIVE_INFINITY, -1.0f}, + {Float.NaN, Float.NaN}, + {Float.MIN_NORMAL, 1.0f}, + {-Float.MIN_NORMAL, -1.0f}, + {0x0.0002P-126f, 1.0f}, + {-0x0.0002P-126f, -1.0f} + }; + + private static final double[][] double_cases = { + {123.4d, 1.0d}, + {-56.7d, -1.0d}, + {7e30d, 1.0d}, + {-0.3e30d, -1.0d}, + {Double.MAX_VALUE, 1.0d}, + {-Double.MAX_VALUE, -1.0d}, + {Double.MIN_VALUE, 1.0d}, + {-Double.MIN_VALUE, -1.0d}, + {0.0d, 0.0d}, + {-0.0d, -0.0d}, + {Double.POSITIVE_INFINITY, 1.0d}, + {Double.NEGATIVE_INFINITY, -1.0d}, + {Double.NaN, Double.NaN}, + {Double.MIN_NORMAL, 1.0d}, + {-Double.MIN_NORMAL, -1.0d}, + {0x0.00000001P-1022, 1.0d}, + {-0x0.00000001P-1022, -1.0d} + }; + + public static void main(String[] args) throws Exception { + float fAccum = 0.0f; + double dAccum = 0.0d; + for (int i = 0; i < 100_000; i++) { + fAccum += floatTest(); + dAccum += doubleTest(); + } + System.out.println("SUCCESS. Accum values: " + fAccum + " and " + dAccum); + } + + private static float floatTest() { + float accum = 0.0f; + for (float[] fcase : float_cases) { + float arg = fcase[0]; + float expected = fcase[1]; + float calculated = Math.signum(arg); + Asserts.assertEQ(expected, calculated, "Unexpected float result"); + accum += calculated; + } + return accum; + } + + private static double doubleTest() { + double accum = 0.0d; + for (double[] dcase : double_cases) { + double arg = dcase[0]; + double expected = dcase[1]; + double calculated = Math.signum(arg); + Asserts.assertEQ(expected, calculated, "Unexpected double result"); + accum += calculated; + } + return accum; + } +}