HalfPrecisionFloatEncoder.java

package co.nstant.in.cbor.encoder;

import java.io.OutputStream;

import co.nstant.in.cbor.CborEncoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.HalfPrecisionFloat;

public class HalfPrecisionFloatEncoder extends AbstractEncoder<HalfPrecisionFloat> {

	public HalfPrecisionFloatEncoder(CborEncoder encoder, OutputStream outputStream) {
		super(encoder, outputStream);
	}

	@Override
	public void encode(HalfPrecisionFloat dataItem) throws CborException {
		write((7 << 5) | 25);
		int bits = fromFloat(dataItem.getValue());
		write((bits >> 8) & 0xFF);
		write((bits >> 0) & 0xFF);
	}

	/**
	 * @see <a href="http://stackoverflow.com/a/6162687">Stack Overflow</a>
	 */

	// returns all higher 16 bits as 0 for all results
	public static int fromFloat(float fval) {
		int fbits = Float.floatToIntBits(fval);
		int sign = fbits >>> 16 & 0x8000; // sign only
		int val = 0x1000 + fbits & 0x7fffffff; // rounded value

		if (val >= 0x47800000) // might be or become NaN/Inf
		{ // avoid Inf due to rounding
			if ((fbits & 0x7fffffff) >= 0x47800000) { // is or must become
														// NaN/Inf
				if (val < 0x7f800000) {// was value but too large
					return sign | 0x7c00; // make it +/-Inf
				}
				return sign | 0x7c00 | // remains +/-Inf or NaN
						(fbits & 0x007fffff) >>> 13; // keep NaN (and
														// Inf) bits
			}
			return sign | 0x7bff; // unrounded not quite Inf
		}
		if (val >= 0x38800000) { // remains normalized value
			return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
		}
		if (val < 0x33000000) { // too small for subnormal
			return sign; // becomes +/-0
		}
		val = (fbits & 0x7fffffff) >>> 23; // tmp exp for subnormal calc
		return sign | ((fbits & 0x7fffff | 0x800000) // add subnormal bit
				+ (0x800000 >>> val - 102) // round depending on cut off
		>>> 126 - val); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
	}

}