/*
* Copyright (c) 2016, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.runtime.floating;
import java.nio.ByteBuffer;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.ValueType;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.llvm.runtime.LLVMPerformance;
@ValueType
public final class LLVM80BitFloat {
private static final int BIT_TO_HEX_FACTOR = 4;
public static final int BIT_WIDTH = 80;
public static final int BYTE_WIDTH = BIT_WIDTH / Byte.SIZE;
private static final int HEX_WIDTH = BIT_WIDTH / BIT_TO_HEX_FACTOR;
private static final int EXPONENT_BIT_WIDTH = 15;
private static final int FRACTION_BIT_WIDTH = 64;
private static final int HEX_DIGITS_FRACTION = FRACTION_BIT_WIDTH / BIT_TO_HEX_FACTOR;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("sign: " + getSign() + "\n");
sb.append("exponent: " + getBinaryString(EXPONENT_BIT_WIDTH, getExponent()) + "\n");
sb.append("fraction: " + getBinaryString(FRACTION_BIT_WIDTH, getFraction()) + " " + getHexString(HEX_DIGITS_FRACTION, getFraction()) + "\n");
return sb.toString();
}
private static String getBinaryString(int bitWidth, long number) {
return String.format("%" + bitWidth + "s", Long.toBinaryString(number)).replace(" ", "0");
}
private static String getHexString(int bitWidth, long number) {
return String.format("%" + bitWidth + "x", number).replace(" ", "0");
}
/**
* Casting a NaN or infinity float to int is not specified. We mimic the "standard" undefined
* behavior and return this constant.
*
* @see <a href=
* "http://stackoverflow.com/questions/10366485/problems-casting-nan-floats-to-int">
* Stackoverflow Problems casting NAN floats to int</a>
*/
private static final byte UNDEFINED_FLOAT_TO_BYTE_VALUE = 0;
private static final short UNDEFINED_FLOAT_TO_SHORT_VALUE = 0;
private static final int UNDEFINED_FLOAT_TO_INT_VALUE = 0x80000000;
private static final long UNDEFINED_FLOAT_TO_LONG_VALUE = 0x8000000000000000L;
private static final long UNDEFINED_DOUBLE_VALUE = 0x80000000_00000000L;
private static final int ALL_ONE_EXPONENT = 0b111111111111111;
private static final LLVM80BitFloat DOUBLE_MINUS_INFINITY_CONVERSION_NUMBER = LLVM80BitFloat.fromRawValues(true, ALL_ONE_EXPONENT, UNDEFINED_DOUBLE_VALUE);
private static final LLVM80BitFloat DOUBLE_INFINITY_CONVERSION_NUMBER = LLVM80BitFloat.fromRawValues(false, ALL_ONE_EXPONENT, UNDEFINED_DOUBLE_VALUE);
private static final LLVM80BitFloat DOUBLE_NAN_CONVERSION_NUMBER = LLVM80BitFloat.fromRawValues(false, ALL_ONE_EXPONENT, 0xc000000000000000L);
public static final LLVM80BitFloat POSITIVE_ZERO = new LLVM80BitFloat(false, 0, 0);
public static final LLVM80BitFloat NEGATIVE_ZERO = new LLVM80BitFloat(true, 0, 0);
public static final LLVM80BitFloat POSITIVE_INFINITY = new LLVM80BitFloat(false, ALL_ONE_EXPONENT, bit(63L));
public static final LLVM80BitFloat NEGATIVE_INFINITY = new LLVM80BitFloat(true, ALL_ONE_EXPONENT, bit(63L));
private static final int EXPLICIT_LEADING_ONE_BITS = 1;
private static final int EXPONENT_BIAS = 16383;
private static final int FLOAT_EXPONENT_BIAS = 127;
private boolean sign;
private int biasedExponent; // 15 bit
private long fraction; // 64 bit
public LLVM80BitFloat(boolean sign, int exponent, long fraction) {
this.sign = sign;
this.biasedExponent = exponent;
this.fraction = fraction;
}
private int getUnbiasedExponent() {
return biasedExponent - EXPONENT_BIAS;
}
private static long bit(int i) {
return 1 << i;
}
private static long bit(long i) {
return 1L << i;
}
public static LLVM80BitFloat fromLong(long val) {
if (val == 0) {
return POSITIVE_ZERO;
}
boolean sign = val < 0;
return fromLong(Math.abs(val), sign);
}
private static LLVM80BitFloat fromLong(long val, boolean sign) {
LLVMPerformance.warn(null, "LLVM80BitFloat:fromLong");
int leadingOnePosition = Long.SIZE - Long.numberOfLeadingZeros(val);
int exponent = EXPONENT_BIAS + (leadingOnePosition - 1);
long fractionMask;
if (leadingOnePosition == Long.SIZE || leadingOnePosition == Long.SIZE - 1) {
fractionMask = 0xffffffff;
} else {
fractionMask = (1L << leadingOnePosition + 1) - 1;
}
long maskedFractionValue = val & fractionMask;
long fraction = maskedFractionValue << (Long.SIZE - leadingOnePosition);
return new LLVM80BitFloat(sign, exponent, fraction);
}
public static LLVM80BitFloat fromUnsignedLong(long val) {
if (val == 0) {
return POSITIVE_ZERO;
}
return fromLong(val, false);
}
public static LLVM80BitFloat fromUnsignedInt(int val) {
if (val == 0) {
return POSITIVE_ZERO;
}
return fromLong(val & BinaryHelper.INT_MASK, false);
}
public static LLVM80BitFloat fromInt(int val) {
if (val == 0) {
return POSITIVE_ZERO;
}
boolean sign = val < 0;
return fromInt(val, sign);
}
private static LLVM80BitFloat fromInt(int val, boolean sign) {
int posVal = Math.abs(val);
int leadingOnePosition = Integer.SIZE - Integer.numberOfLeadingZeros(posVal);
int exponent = EXPONENT_BIAS + (leadingOnePosition - 1);
long fractionMask = (1L << leadingOnePosition + 1) - 1;
long maskedFractionValue = posVal & fractionMask;
long fraction = maskedFractionValue << (Long.SIZE - leadingOnePosition);
return new LLVM80BitFloat(sign, exponent, fraction);
}
private static boolean getBit(int position, long posVal) {
long l = posVal >>> position;
return (l & 1) != 0;
}
public boolean isZero() {
return isPositiveZero() || isNegativeZero();
}
private boolean isPositiveZero() {
return equals(POSITIVE_ZERO);
}
private boolean isNegativeZero() {
return equals(NEGATIVE_ZERO);
}
public static LLVM80BitFloat fromDouble(double val) {
boolean sign = val < 0;
if (DoubleHelper.isPositiveZero(val)) {
return POSITIVE_ZERO;
} else if (DoubleHelper.isNegativeZero(val)) {
return NEGATIVE_ZERO;
} else if (DoubleHelper.isPositiveInfinty(val)) {
return DOUBLE_INFINITY_CONVERSION_NUMBER;
} else if (DoubleHelper.isNegativeInfinity(val)) {
return DOUBLE_MINUS_INFINITY_CONVERSION_NUMBER;
} else if (DoubleHelper.isNaN(val)) {
return DOUBLE_NAN_CONVERSION_NUMBER;
} else {
long rawValue = Double.doubleToRawLongBits(val);
int doubleExponent = DoubleHelper.getUnbiasedExponent(val);
int biasedExponent = doubleExponent + EXPONENT_BIAS;
long leadingOne = (long) EXPLICIT_LEADING_ONE_BITS << (FRACTION_BIT_WIDTH - 1);
long doubleFraction = rawValue & DoubleHelper.FRACTION_MASK;
long shiftedDoubleFraction = doubleFraction << (FRACTION_BIT_WIDTH - DoubleHelper.DOUBLE_FRACTION_BIT_WIDTH - EXPLICIT_LEADING_ONE_BITS);
long fraction = shiftedDoubleFraction | leadingOne;
return LLVM80BitFloat.fromRawValues(sign, biasedExponent, fraction);
}
}
private long getFractionAsLong() {
return fraction >>> (FRACTION_BIT_WIDTH - getUnbiasedExponent() - EXPLICIT_LEADING_ONE_BITS);
}
private long compareNoSign(LLVM80BitFloat val) {
if (getExponent() != val.getExponent()) {
return getExponent() - val.getExponent();
} else {
return (getFraction() - val.getFraction());
}
}
public LLVM80BitFloat add(LLVM80BitFloat right) {
double doubleValue = getDoubleValue();
double doubleValue2 = right.getDoubleValue();
return fromDouble(doubleValue + doubleValue2);
}
@SuppressWarnings("unused")
private LLVM80BitFloat add2(LLVM80BitFloat right) {
int leftExponent = getExponent();
int rightExponent = right.getExponent();
long leftFraction = getFraction();
long rightFraction = right.getFraction();
int shiftAmount = Math.abs(leftExponent - rightExponent);
if (leftExponent < rightExponent) {
leftFraction >>>= shiftAmount;
leftExponent = rightExponent;
} else {
rightFraction >>>= shiftAmount;
rightExponent = leftExponent;
}
boolean newSign;
if (getSign() == right.getSign()) {
newSign = getSign();
} else {
newSign = compareNoSign(right) < 0 ? right.getSign() : getSign();
}
boolean addition = getSign() == right.getSign();
long resultLo;
long resultHi;
long leftFractionLowerPart = leftFraction & BinaryHelper.getBitMask(Integer.SIZE);
long rightFractionLowerPart = rightFraction & BinaryHelper.getBitMask(Integer.SIZE);
long leftFractionHigherPart = leftFraction >>> Integer.SIZE;
long rightFractionHigherPart = rightFraction >>> Integer.SIZE;
if (addition) {
resultLo = -leftFractionLowerPart + rightFractionLowerPart;
long overFlowLowerPart = resultLo >>> Integer.SIZE;
resultHi = leftFractionHigherPart + rightFractionHigherPart + overFlowLowerPart;
} else if (getSign()) { // left is negative
resultLo = -leftFractionLowerPart + rightFractionLowerPart;
long overFlowLowerPart = resultLo >>> Integer.SIZE;
resultHi = -leftFractionHigherPart - rightFractionHigherPart - overFlowLowerPart;
} else {
resultLo = leftFractionLowerPart - rightFractionLowerPart;
long overFlowLowerPart = resultLo >>> Integer.SIZE;
resultHi = leftFractionHigherPart - rightFractionHigherPart - overFlowLowerPart;
}
int overFlow = (int) (resultHi >>> Integer.SIZE);
if (overFlow > 0) {
resultHi = resultHi >>> overFlow;
long lostBits = resultHi & overFlow;
long shiftedLostBits = lostBits << Integer.SIZE - overFlow;
resultLo = resultLo >>> overFlow | shiftedLostBits;
}
int newExponent = leftExponent + overFlow;
long newFraction = resultLo + resultHi << Integer.SIZE;
return LLVM80BitFloat.fromRawValues(newSign, newExponent, newFraction);
}
public LLVM80BitFloat sub(LLVM80BitFloat right) {
return add(right.negate());
}
public LLVM80BitFloat mul(LLVM80BitFloat right) {
return fromDouble(getDoubleValue() * right.getDoubleValue());
}
public LLVM80BitFloat div(LLVM80BitFloat right) {
return fromDouble(getDoubleValue() / right.getDoubleValue());
}
public LLVM80BitFloat rem(LLVM80BitFloat right) {
return fromDouble(getDoubleValue() % right.getDoubleValue());
}
public boolean isPositiveInfinity() {
return this.equals(POSITIVE_INFINITY);
}
public boolean isNegativeInfinity() {
return equals(NEGATIVE_INFINITY);
}
public boolean isInfinity() {
return isPositiveInfinity() || isNegativeInfinity();
}
public boolean isQNaN() {
// Checkstyle: stop magic number name check
if (getExponent() == ALL_ONE_EXPONENT) {
if (!getBit(63, getFraction())) {
if (getBit(62, getFraction())) {
return true;
}
}
}
// Checkstyle: resume magic number name check
return false;
}
public boolean isOrdered() {
return !isQNaN();
}
int compareOrdered(LLVM80BitFloat val) {
if (isNegativeInfinity()) {
if (val.isNegativeInfinity()) {
return 0;
} else {
return -1;
}
}
if (val.isNegativeInfinity()) {
if (isNegativeInfinity()) {
return 0;
} else {
return 1;
}
}
if (getSign() == val.getSign()) {
int expDifference = getExponent() - val.getExponent();
if (expDifference == 0) {
long fractionDifference = getFraction() - val.getFraction();
if (fractionDifference == 0) {
return 0;
} else if (fractionDifference < 0) {
return -1;
} else {
return 1;
}
} else {
return expDifference;
}
} else {
if (getSign()) {
return -1;
} else {
return 1;
}
}
}
public short getExponent() {
return (short) biasedExponent;
}
public long getFraction() {
return fraction;
}
public long getFractionWithoutImplicitZero() {
return fraction << 1;
}
public boolean getSign() {
return sign;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof LLVM80BitFloat)) {
return false;
}
LLVM80BitFloat other = ((LLVM80BitFloat) obj);
return getSign() == other.getSign() && getExponent() == other.getExponent() && getFraction() == other.getFraction();
}
@Override
public int hashCode() {
return Arrays.hashCode(getBytes());
}
public byte[] getBytes() {
ByteBuffer bb = ByteBuffer.allocate(BYTE_WIDTH);
short signWithExponent = getExponent();
short signBit = sign ? (short) bit(Short.SIZE - 1) : 0;
signWithExponent |= signBit;
bb.putShort(signWithExponent);
bb.putLong(getFraction());
return bb.array();
}
public static LLVM80BitFloat fromBytes(byte[] bytes) {
assert bytes.length == BYTE_WIDTH;
ByteBuffer bb = ByteBuffer.wrap(bytes);
short readShort = bb.getShort();
int exponent = readShort & BinaryHelper.getBitMask(EXPONENT_BIT_WIDTH);
long fraction = bb.getLong();
boolean signSet = getBit(Short.SIZE, readShort);
return LLVM80BitFloat.fromRawValues(signSet, exponent, fraction);
}
// get value
public byte getByteValue() {
if (isQNaN() || isInfinity()) {
return UNDEFINED_FLOAT_TO_BYTE_VALUE;
} else {
long value = getFractionAsLong();
return (byte) (sign ? -value : value);
}
}
public short getShortValue() {
if (isQNaN() || isInfinity()) {
return UNDEFINED_FLOAT_TO_SHORT_VALUE;
} else {
long value = getFractionAsLong();
return (short) (sign ? -value : value);
}
}
public int getIntValue() {
if (isQNaN() || isInfinity()) {
return UNDEFINED_FLOAT_TO_INT_VALUE;
}
int value = (int) getFractionAsLong();
return sign ? -value : value;
}
public long getLongValue() {
if (isQNaN() || isInfinity()) {
return UNDEFINED_FLOAT_TO_LONG_VALUE;
} else {
long value = getFractionAsLong();
return sign ? -value : value;
}
}
public float getFloatValue() {
if (isPositiveZero()) {
return FloatHelper.POSITIVE_ZERO;
} else if (isNegativeZero()) {
return FloatHelper.NEGATIVE_ZERO;
} else if (isPositiveInfinity()) {
return FloatHelper.POSITIVE_INFINITY;
} else if (isNegativeInfinity()) {
return FloatHelper.NEGATIVE_INFINITY;
} else if (isQNaN()) {
return FloatHelper.NaN;
} else {
int floatExponent = getUnbiasedExponent() + FLOAT_EXPONENT_BIAS;
long longFraction = (getFractionWithoutImplicitZero()) >>> (FRACTION_BIT_WIDTH - FloatHelper.FLOAT_FRACTION_BIT_WIDTH);
int floatFraction = (int) longFraction;
int shiftedSignBit = (getSign() ? 1 : 0) << FloatHelper.FLOAT_SIGN_POS;
int shiftedExponent = floatExponent << FloatHelper.FLOAT_FRACTION_BIT_WIDTH;
int rawVal = floatFraction | shiftedExponent | shiftedSignBit;
return Float.intBitsToFloat(rawVal);
}
}
public double getDoubleValue() {
if (isPositiveZero()) {
return DoubleHelper.POSITIVE_ZERO;
} else if (isNegativeZero()) {
return DoubleHelper.NEGATIVE_ZERO;
} else if (isPositiveInfinity()) {
return DoubleHelper.POSITIVE_INFINITY;
} else if (isNegativeInfinity()) {
return DoubleHelper.NEGATIVE_INFINITY;
} else if (isQNaN()) {
return DoubleHelper.NaN;
} else {
int doubleExponent = getUnbiasedExponent() + DoubleHelper.DOUBLE_EXPONENT_BIAS;
long doubleFraction = (getFractionWithoutImplicitZero()) >>> (FRACTION_BIT_WIDTH - DoubleHelper.DOUBLE_FRACTION_BIT_WIDTH);
long shiftedSignBit = (getSign() ? 1L : 0L) << DoubleHelper.DOUBLE_SIGN_POS;
long shiftedExponent = (long) doubleExponent << DoubleHelper.DOUBLE_FRACTION_BIT_WIDTH;
long rawVal = doubleFraction | shiftedExponent | shiftedSignBit;
return Double.longBitsToDouble(rawVal);
}
}
public LLVM80BitFloat negate() {
return new LLVM80BitFloat(!getSign(), getExponent(), getFraction());
}
public static LLVM80BitFloat fromByte(byte from) {
return fromInt(from);
}
public static LLVM80BitFloat fromUnsignedByte(byte from) {
return fromInt(from & Byte.MIN_VALUE);
}
public static LLVM80BitFloat fromShort(short from) {
return fromInt(from);
}
public static LLVM80BitFloat fromRawValues(boolean sign, int exp, long fraction) {
return new LLVM80BitFloat(sign, exp, fraction);
}
@ExplodeLoop
public static boolean areOrdered(LLVM80BitFloat... vals) {
CompilerAsserts.compilationConstant(vals.length);
for (LLVM80BitFloat val : vals) {
if (!val.isOrdered()) {
return false;
}
}
return true;
}
public static int compare(LLVM80BitFloat val1, LLVM80BitFloat val2) {
return val1.compareOrdered(val2);
}
public static LLVM80BitFloat fromString(String stringValue) {
if (stringValue.length() != HEX_WIDTH) {
throw new IllegalArgumentException("unexpected length of input string!");
}
return fromBytes(DatatypeConverter.parseHexBinary(stringValue));
}
}