Oracle’s internal formats.3

Negative values between -1 and 0

A value of -0.00412
40 we can use 0x40 xor 0x7f = 0x3f (which is the same exponent as for the positive value)
3C we can use 0x65 (101) – 0x3C = 0x29 (41)
51 we can use 0x65 (101) – 0x51 = 0x14 (20)
66 the minus sign

A value of -0.412
3f xor 7f = 40 (positive equivalent is 0xC0, highest bit differs) = -2
3c should be 41
51 should be 20
66

Seems to work.

+/- infinity

There is a byte array

FF
65

which is used for numbers larger than 9.999..9*10^125.

Smaller values then -9.999..9*10^125 will be stored as a single byte

00

Both values could be interpreted as +/- infinity.

Borders

Values with a smaller exponent than -130 will be stored as zero (one byte 0x80).

I have written the algorithms into a Java class:

import java.math.BigDecimal;
import java.util.Arrays;

public class OracleNumber {

	private final BigDecimal value;

	public static final OracleNumber ZERO = new OracleNumber(BigDecimal.ZERO);
	public static final OracleNumber POSITIVE_INFINITY = new OracleNumber(new BigDecimal("1E200"));
	public static final OracleNumber NEGATIVE_INFINITY = new OracleNumber(new BigDecimal("-1E200"));

	private OracleNumber(BigDecimal val) {
		this.value = val;
	}

	private OracleNumber(boolean negative, String mantissa, int exponent) {
		this.value = new BigDecimal((negative ? "-" : "") + mantissa + "0E" + exponent).stripTrailingZeros();
	}

	private static boolean isZero(short[] array) {
		return array.length == 1 && array[0] == 0x80;
	}

	private static boolean isNegative(short[] array) {
		return ((array[0] & 0x80) == 0);
	}

	private static boolean hasPositiveExponent(short[] array) {
		return (array[0] & 0x40) != 0;
	}

	private static boolean isPositiveInfinity(short[] array) {
		return (array.length == 2 && array[0] == 0xFF && array[1] == 0x65);
	}

	private static boolean isNegativeInfinity(short[] array) {
		return (array.length == 1 && array[0] == 0x0);
	}

	public static OracleNumber newInstance(short[] array) {

		short[] number = Arrays.copyOf(array, array.length);

		if (isZero(number)) {
			return OracleNumber.ZERO;
		}

		if (isNegativeInfinity(number)) {
			return OracleNumber.NEGATIVE_INFINITY;
		}

		if (isPositiveInfinity(number)) {
			return OracleNumber.POSITIVE_INFINITY;
		}

		if (isNegative(number)) {
			// convert exponent into the equivalent for positive numbers
			number[0] = (short) ((number[0] | 0x80) ^ 0x7F);
		}

		int exponent;

		if (hasPositiveExponent(number)) {
			exponent = ((number[0] & 0x3F) << 1) - 2;
		} else {
			exponent = -1 * (((number[0] ^ 0xBF) << 1) + 4);
		}

		// calculating mantissa
		String mantissa = "";
		for (int i = 1; i < number.length; i++) {
			if (isNegative(array)) {
				mantissa += (number[i] > 0x65) ? "" : (0x65 - number[i]);
			} else {
				mantissa += (number[i] - 1);
			}
			if (i == 1) {
				mantissa += ".";
			}
		}

		return new OracleNumber(isNegative(array), mantissa, exponent);
	}

	@Override
	public String toString() {
		return value.toString();
	}

	public BigDecimal getValue() {
		return new BigDecimal(value.toString());
	}
}

Leave a Reply