diff options
author | 2016-02-25 11:32:32 -0800 | |
---|---|---|
committer | 2016-02-26 09:16:07 -0800 | |
commit | 2a6aad9d388bd29bff04aeec3eb9429d436d1873 (patch) | |
tree | a5fa70df33d410af1be1064c460b6e34f07caf93 | |
parent | 9ec486cc08ad658eee33f9bde405b97502562840 (diff) |
Implement fp to bits methods as intrinsics.
Rationale:
Better optimization, better performance.
Results on libcore benchmark:
Most gain is from moving the invariant call out of the loop
after we detect everything is a side-effect free intrinsic.
But generated code in general case is much cleaner too.
Before:
timeFloatToIntBits() in 181 ms.
timeFloatToRawIntBits() in 35 ms.
timeDoubleToLongBits() in 208 ms.
timeDoubleToRawLongBits() in 35 ms.
After:
timeFloatToIntBits() in 36 ms.
timeFloatToRawIntBits() in 35 ms.
timeDoubleToLongBits() in 35 ms.
timeDoubleToRawLongBits() in 34 ms.
bug=11548336
Change-Id: I6e001bd3708e800bd75a82b8950fb3a0fc01766e
-rw-r--r-- | compiler/dex/quick/dex_file_method_inliner.cc | 11 | ||||
-rw-r--r-- | compiler/dex/quick/dex_file_method_inliner.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 84 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_arm.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_arm64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_list.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_mips.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_mips64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_x86.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_x86_64.cc | 2 | ||||
-rw-r--r-- | runtime/quick/inline_method_analyser.h | 2 | ||||
-rw-r--r-- | test/577-checker-fp2int/expected.txt | 1 | ||||
-rw-r--r-- | test/577-checker-fp2int/info.txt | 1 | ||||
-rw-r--r-- | test/577-checker-fp2int/src/Main.java | 122 |
15 files changed, 222 insertions, 19 deletions
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 209f101199..ad4ddadd2f 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -37,6 +37,8 @@ namespace { // anonymous namespace static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicDoubleCvt true, // kIntrinsicFloatCvt + true, // kIntrinsicFloat2Int + true, // kIntrinsicDouble2Long true, // kIntrinsicFloatIsInfinite true, // kIntrinsicDoubleIsInfinite true, // kIntrinsicFloatIsNaN @@ -106,6 +108,8 @@ static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop, "arraysize of kIntrinsicIsStatic unexpected"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static"); @@ -277,6 +281,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "equals", // kNameCacheEquals "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty + "floatToIntBits", // kNameCacheFloatToIntBits + "doubleToLongBits", // kNameCacheDoubleToLongBits "isInfinite", // kNameCacheIsInfinite "isNaN", // kNameCacheIsNaN "indexOf", // kNameCacheIndexOf @@ -472,6 +478,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint), + INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0), + INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0), + INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0), INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0), INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0), @@ -791,6 +800,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { intrinsic.d.data & kIntrinsicFlagIsOrdered); case kIntrinsicSystemArrayCopyCharArray: return backend->GenInlinedArrayCopyCharArray(info); + case kIntrinsicFloat2Int: + case kIntrinsicDouble2Long: case kIntrinsicFloatIsInfinite: case kIntrinsicDoubleIsInfinite: case kIntrinsicFloatIsNaN: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 59b8a533ae..b465db2c54 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -190,6 +190,8 @@ class DexFileMethodInliner { kNameCacheEquals, kNameCacheGetCharsNoCheck, kNameCacheIsEmpty, + kNameCacheFloatToIntBits, + kNameCacheDoubleToLongBits, kNameCacheIsInfinite, kNameCacheIsNaN, kNameCacheIndexOf, diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 13d3f752c3..f8a9a94e62 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); + void SimplifyFP2Int(HInvoke* invoke); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); } +void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + HInstruction* x = invoke->InputAt(0); + Primitive::Type type = x->GetType(); + // Set proper bit pattern for NaN and replace intrinsic with raw version. + HInstruction* nan; + if (type == Primitive::kPrimDouble) { + nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); + invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + nan = GetGraph()->GetIntConstant(0x7fc00000); + invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } + // Test IsNaN(x), which is the same as x != x. + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); + // Select between the two. + HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc); + invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); + invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { - if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - SimplifyStringEquals(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { - SimplifySystemArrayCopy(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight || - instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) { - SimplifyRotate(instruction, false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft || - instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) { - SimplifyRotate(instruction, true); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare || - instruction->GetIntrinsic() == Intrinsics::kLongCompare) { - SimplifyCompare(instruction, /* is_signum */ false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || - instruction->GetIntrinsic() == Intrinsics::kLongSignum) { - SimplifyCompare(instruction, /* is_signum */ true); - } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || - instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { - SimplifyIsNaN(instruction); + switch (instruction->GetIntrinsic()) { + case Intrinsics::kStringEquals: + SimplifyStringEquals(instruction); + break; + case Intrinsics::kSystemArrayCopy: + SimplifySystemArrayCopy(instruction); + break; + case Intrinsics::kIntegerRotateRight: + case Intrinsics::kLongRotateRight: + SimplifyRotate(instruction, false); + break; + case Intrinsics::kIntegerRotateLeft: + case Intrinsics::kLongRotateLeft: + SimplifyRotate(instruction, true); + break; + case Intrinsics::kIntegerCompare: + case Intrinsics::kLongCompare: + SimplifyCompare(instruction, /* is_signum */ false); + break; + case Intrinsics::kIntegerSignum: + case Intrinsics::kLongSignum: + SimplifyCompare(instruction, /* is_signum */ true); + break; + case Intrinsics::kFloatIsNaN: + case Intrinsics::kDoubleIsNaN: + SimplifyIsNaN(instruction); + break; + case Intrinsics::kFloatFloatToIntBits: + case Intrinsics::kDoubleDoubleToLongBits: + SimplifyFP2Int(instruction); + break; + default: + break; } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 316e86b4c9..3ed0278871 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicFloatCvt: return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + case kIntrinsicFloat2Int: + return Intrinsics::kFloatFloatToIntBits; + case kIntrinsicDouble2Long: + return Intrinsics::kDoubleDoubleToLongBits; // Floating-point tests. case kIntrinsicFloatIsInfinite: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8cbdcbbcaf..555bd7fa6b 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1944,6 +1944,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b5f15fe22d..f6b4779575 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1626,6 +1626,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 88217b308e..e1aea924cf 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -23,10 +23,12 @@ #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2f183c3a62..a737d8100a 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index bd4f5329da..ca2652b74a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 260a8773fb..0df4553f56 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 93e8c00e5a..2a9e684d11 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 0b09a70be4..7e84b405e7 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -37,6 +37,8 @@ class MethodVerifier; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, + kIntrinsicFloat2Int, + kIntrinsicDouble2Long, kIntrinsicFloatIsInfinite, kIntrinsicDoubleIsInfinite, kIntrinsicFloatIsNaN, diff --git a/test/577-checker-fp2int/expected.txt b/test/577-checker-fp2int/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/577-checker-fp2int/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/577-checker-fp2int/info.txt b/test/577-checker-fp2int/info.txt new file mode 100644 index 0000000000..d22a0eab9d --- /dev/null +++ b/test/577-checker-fp2int/info.txt @@ -0,0 +1 @@ +Unit test for float/double to raw bits conversions. diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java new file mode 100644 index 0000000000..e3f1230beb --- /dev/null +++ b/test/577-checker-fp2int/src/Main.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: int Main.f2int(float) instruction_simplifier (before) + /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:FloatFloatToIntBits + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: int Main.f2int(float) instruction_simplifier (after) + /// CHECK-DAG: <<Raw:i\d+>> InvokeStaticOrDirect [<<Arg:f\d+>>] intrinsic:FloatFloatToRawIntBits + /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>] + /// CHECK-DAG: <<Result:i\d+>> Select [<<Raw>>,{{i\d+}},<<Cond>>] + /// CHECK-DAG: Return [<<Result>>] + private static int f2int(float f) { + return Float.floatToIntBits(f); + } + + /// CHECK-START: long Main.d2long(double) instruction_simplifier (before) + /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:DoubleDoubleToLongBits + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: long Main.d2long(double) instruction_simplifier (after) + /// CHECK-DAG: <<Raw:j\d+>> InvokeStaticOrDirect [<<Arg:d\d+>>] intrinsic:DoubleDoubleToRawLongBits + /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>] + /// CHECK-DAG: <<Result:j\d+>> Select [<<Raw>>,{{j\d+}},<<Cond>>] + /// CHECK-DAG: Return [<<Result>>] + private static long d2long(double d) { + return Double.doubleToLongBits(d); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectEquals32(0xff800000, f2int(Float.NEGATIVE_INFINITY)); + expectEquals32(0xbf800000, f2int(-1.0f)); + expectEquals32(0x80000000, f2int(-0.0f)); + expectEquals32(0x00000000, f2int(+0.0f)); + expectEquals32(0x3f800000, f2int(+1.0f)); + expectEquals32(0x7f800000, f2int(Float.POSITIVE_INFINITY)); + + // A few others. + for (int i = 0; i <= 100; i++) { + expectEquals32(i, f2int(Float.intBitsToFloat(i))); + } + + // A few NaN numbers. + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(0x7fc00000, f2int(fvals[i])); + } + + // A few distinct numbers. + expectEquals64(0xfff0000000000000L, d2long(Double.NEGATIVE_INFINITY)); + expectEquals64(0xbff0000000000000L, d2long(-1.0d)); + expectEquals64(0x8000000000000000L, d2long(-0.0d)); + expectEquals64(0x0000000000000000L, d2long(+0.0d)); + expectEquals64(0x3ff0000000000000L, d2long(+1.0d)); + expectEquals64(0x7ff0000000000000L, d2long(Double.POSITIVE_INFINITY)); + + // A few others. + for (long l = 0; l <= 100; l++) { + expectEquals64(l, d2long(Double.longBitsToDouble(l))); + } + + // A few NaN numbers. + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(0x7ff8000000000000L, d2long(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + + Integer.toHexString(expected) + + ", found: " + + Integer.toHexString(result)); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + + Long.toHexString(expected) + + ", found: " + + Long.toHexString(result)); + } + } +} |