diff --git a/src/java.base/share/classes/java/lang/Float16.java b/src/java.base/share/classes/java/lang/Float16.java index 22cae26a049..5be61c89048 100644 --- a/src/java.base/share/classes/java/lang/Float16.java +++ b/src/java.base/share/classes/java/lang/Float16.java @@ -76,10 +76,7 @@ public final class Float16 private static final long serialVersionUID = 16; // Not needed for a value class? // Functionality for future consideration: - // float16ToShortBits that normalizes NaNs, c.f. floatToIntBits vs floatToRawIntBits - // copysign // IEEEremainder / remainder operator remainder - // signum /** * Returns a {@code Float16} instance wrapping IEEE 754 binary16 @@ -577,7 +574,8 @@ public double doubleValue() { /** * Returns a representation of the specified floating-point value - * according to the IEEE 754 floating-point binary16 bit layout. + * according to the IEEE 754 floating-point binary16 bit layout, + * preserving Not-a-Number (NaN) values. * * @param f16 a {@code Float16} floating-point number. * @return the bits that represent the floating-point number. @@ -589,6 +587,23 @@ public static short float16ToRawShortBits(Float16 f16) { return f16.value; } + /** + * Returns a representation of the specified floating-point value + * according to the IEEE 754 floating-point binary16 bit layout. + * + * @param fp16 a {@code Float16} floating-point number. + * @return the bits that represent the floating-point number. + * + * @see Float#floatToIntBits(float) + * @see Double#doubleToLongBits(double) + */ + public static short float16ToShortBits(Float16 fp16) { + if (!isNaN(fp16)) { + return float16ToRawShortBits(fp16); + } + return 0x7e00; + } + /** * Returns the {@code Float16} value corresponding to a given bit * representation. @@ -1313,4 +1328,48 @@ public static Float16 scalb(Float16 v, int scaleFactor) { * Double.longBitsToDouble((long) (scaleFactor + DoubleConsts.EXP_BIAS) << Double.PRECISION - 1)); } + /** + * Returns the first floating-point argument with the sign of the + * second floating-point argument. + * This method does not require NaN {@code sign} + * arguments to be treated as positive values; implementations are + * permitted to treat some NaN arguments as positive and other NaN + * arguments as negative to allow greater performance. + * + * @apiNote + * This method corresponds to the copySign operation defined in + * IEEE 754. + * + * @param magnitude the parameter providing the magnitude of the result + * @param sign the parameter providing the sign of the result + * @return a value with the magnitude of {@code magnitude} + * and the sign of {@code sign}. + */ + public static Float16 copySign(Float16 magnitude, Float16 sign) { + return shortBitsToFloat16((short) ((float16ToRawShortBits(sign) & + (Float16Consts.SIGN_BIT_MASK)) | + (float16ToRawShortBits(magnitude) & + (Float16Consts.EXP_BIT_MASK | + Float16Consts.SIGNIF_BIT_MASK)))); + } + + /** + * Returns the signum function of the argument; zero if the argument + * is zero, 1.0 if the argument is greater than zero, -1.0 if the + * argument is less than zero. + * + * <p>Special Cases: + * <ul> + * <li> If the argument is NaN, then the result is NaN. + * <li> If the argument is positive zero or negative zero, then the + * result is the same as the argument. + * </ul> + * + * @param f the floating-point value whose signum is to be returned + * @return the signum function of the argument + */ + public static Float16 signum(Float16 f) { + return (f.floatValue() == 0.0f || isNaN(f)) ? f : copySign(valueOf(1), f); + } + } diff --git a/test/jdk/java/lang/Math/IeeeRecommendedTests.java b/test/jdk/java/lang/Math/IeeeRecommendedTests.java index 956904ee079..cca85734ffa 100644 --- a/test/jdk/java/lang/Math/IeeeRecommendedTests.java +++ b/test/jdk/java/lang/Math/IeeeRecommendedTests.java @@ -871,6 +871,69 @@ public static int testDoubleBooleanMethods() { /* ******************** copySign tests******************************** */ + public static int testFloat16CopySign() { + int failures = 0; + + // testCases[0] are logically positive numbers; + // testCases[1] are negative numbers. + float testCases [][] = { + {+0.0f, + Float16.MIN_VALUE.floatValue(), + Float16_MAX_SUBNORMALmm.floatValue(), + Float16_MAX_SUBNORMAL.floatValue(), + Float16.MIN_NORMAL.floatValue(), + 1.0f, + 3.0f, + Float16_MAX_VALUEmm.floatValue(), + Float16.MAX_VALUE.floatValue(), + infinityF16.floatValue(), + }, + {-infinityF16.floatValue(), + -Float16.MAX_VALUE.floatValue(), + -3.0f, + -1.0f, + -Float16.MIN_NORMAL.floatValue(), + -Float16_MAX_SUBNORMALmm.floatValue(), + -Float16_MAX_SUBNORMAL.floatValue(), + -Float16.MIN_VALUE.floatValue(), + -0.0f} + }; + + float NaNs[] = {Float16.shortBitsToFloat16((short) 0x7e00).floatValue(), // "positive" NaN + Float16.shortBitsToFloat16((short) 0xfe00).floatValue()}; // "negative" NaN + + // Tests shared between raw and non-raw versions + for(int i = 0; i < 2; i++) { + for(int j = 0; j < 2; j++) { + for(int m = 0; m < testCases[i].length; m++) { + for(int n = 0; n < testCases[j].length; n++) { + // copySign(magnitude, sign) + failures+=Tests.test("Float16.copySign(Float16,Float16)", + Float16.valueOf(testCases[i][m]),Float16.valueOf(testCases[j][n]), + Float16.copySign(Float16.valueOf(testCases[i][m]), Float16.valueOf(testCases[j][n])), + Float16.valueOf((j==0?1.0f:-1.0f)*Math.abs(testCases[i][m])) ); + } + } + } + } + + // For rawCopySign, NaN may effectively have either sign bit + // while for copySign NaNs are treated as if they always have + // a zero sign bit (i.e. as positive numbers) + for(int i = 0; i < 2; i++) { + for(int j = 0; j < NaNs.length; j++) { + for(int m = 0; m < testCases[i].length; m++) { + // copySign(magnitude, sign) + + failures += (Float16.abs(Float16.copySign(Float16.valueOf(testCases[i][m]), Float16.valueOf(NaNs[j]))).floatValue() == + Float16.abs(Float16.valueOf(testCases[i][m])).floatValue()) ? 0:1; + } + } + } + + return failures; + } + public static int testFloatCopySign() { int failures = 0; @@ -1892,6 +1955,38 @@ public static int testDoubleUlp() { return failures; } + public static int testFloat16Signum() { + int failures = 0; + float testCases [][] = { + {NaNf16.floatValue(), NaNf16.floatValue()}, + {-infinityF16.floatValue(), -1.0f}, + {-Float16.MAX_VALUE.floatValue(), -1.0f}, + {-Float16.MIN_NORMAL.floatValue(), -1.0f}, + {-1.0f, -1.0f}, + {-2.0f, -1.0f}, + {-Float16_MAX_SUBNORMAL.floatValue(), -1.0f}, + {-Float16.MIN_VALUE.floatValue(), -1.0f}, + {-0.0f, -0.0f}, + {+0.0f, +0.0f}, + {Float16.MIN_VALUE.floatValue(), 1.0f}, + {Float16_MAX_SUBNORMALmm.floatValue(), 1.0f}, + {Float16_MAX_SUBNORMAL.floatValue(), 1.0f}, + {Float16.MIN_NORMAL.floatValue(), 1.0f}, + {1.0f, 1.0f}, + {2.0f, 1.0f}, + {Float16_MAX_VALUEmm.floatValue(), 1.0f}, + {Float16.MAX_VALUE.floatValue(), 1.0f}, + {infinityF16.floatValue(), 1.0f} + }; + + for(int i = 0; i < testCases.length; i++) { + failures+=Tests.test("Float16.signum(Float16)", + Float16.valueOf(testCases[i][0]), Float16.signum(Float16.valueOf(testCases[i][0])), Float16.valueOf(testCases[i][1])); + } + + return failures; + } + public static int testFloatSignum() { int failures = 0; float testCases [][] = { @@ -1981,6 +2076,7 @@ public static void main(String... argv) { failures += testFloatBooleanMethods(); failures += testDoubleBooleanMethods(); + failures += testFloat16CopySign(); failures += testFloatCopySign(); failures += testDoubleCopySign(); @@ -1991,6 +2087,7 @@ public static void main(String... argv) { failures += testFloatUlp(); failures += testDoubleUlp(); + failures += testFloat16Signum(); failures += testFloatSignum(); failures += testDoubleSignum(); diff --git a/test/jdk/java/lang/Math/Tests.java b/test/jdk/java/lang/Math/Tests.java index cacda19be88..07b4047c140 100644 --- a/test/jdk/java/lang/Math/Tests.java +++ b/test/jdk/java/lang/Math/Tests.java @@ -500,6 +500,21 @@ public static int test(String testName, } } + public static int test(String testName, + Float16 input1, Float16 input2, + Float16 result, Float16 expected) { + if (Float16.compare(expected, result ) != 0) { + System.err.println("Failure for " + testName + ":\n" + + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + + input2 + "\t(" + toHexString(input2) + ")\n" + + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + + "\tgot " + result + "\t(" + toHexString(result) + ")."); + return 1; + } else { + return 0; + } + } + public static int test(String testName, double input1, int input2, double result, double expected) {