HalfFloatArray.st
author Claus Gittinger <cg@exept.de>
Mon, 18 Mar 2019 10:59:35 +0100
changeset 4878 5d852ae34bc9
parent 4835 8f8949b8bac9
child 4888 1b19c584754c
permissions -rw-r--r--
#FEATURE by cg class: SftpURI comment/format in: #readStreamDo: changed: #connectThenDo: return the value of the block

"
 COPYRIGHT (c) 2014 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic2' }"

"{ NameSpace: Smalltalk }"

UnboxedFloatArray variableWordSubclass:#HalfFloatArray
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'Collections-Arrayed'
!

!HalfFloatArray primitiveFunctions!
%{
typedef unsigned short halffloat;
typedef unsigned short uint16;
typedef unsigned int   uint32;

//
// convert a halffloat (16-bit float) to a float
//
float
__STX_halffloat_to_float(halffloat h) {
	int e;
	uint16 hs, he, hm;
	uint32 xs, xe, xm;
	int32 xes;
	union {
	    uint32 u32;
	    float f32;
	} u;

	if( (h & 0x7FFFu) == 0 ) {  // Signed zero
	    u.u32 = ((uint32) h) << 16;  // Return the signed zero
	} else { // Not zero
	    hs = h & 0x8000u;  // Pick off sign bit
	    he = h & 0x7C00u;  // Pick off exponent bits
	    hm = h & 0x03FFu;  // Pick off mantissa bits
	    if( he == 0 ) {  // Denormal will convert to normalized
		e = -1; // The following loop figures out how much extra to adjust the exponent
		do {
		    e++;
		    hm <<= 1;
		} while( (hm & 0x0400u) == 0 ); // Shift until leading bit overflows into exponent bit
		xs = ((uint32) hs) << 16; // Sign bit
		xes = ((uint32) (he >> 10)) - 15 + 127 - e; // Exponent unbias the halfp, then bias the single
		xe = (uint32) (xes << 23); // Exponent
		xm = ((uint32) (hm & 0x03FFu)) << 13; // Mantissa
		u.u32 = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits
	    } else if( he == 0x7C00u ) {  // Inf or NaN (all the exponent bits are set)
		if( hm == 0 ) { // If mantissa is zero ...
		    u.u32 = (((uint32) hs) << 16) | ((uint32) 0x7F800000u); // Signed Inf
		} else {
		    u.u32 = (uint32) 0xFFC00000u; // NaN, only 1st mantissa bit set
		}
	    } else { // Normalized number
		xs = ((uint32) hs) << 16; // Sign bit
		xes = ((uint32) (he >> 10)) - 15 + 127; // Exponent unbias the halfp, then bias the single
		xe = (uint32) (xes << 23); // Exponent
		xm = ((uint32) hm) << 13; // Mantissa
		u.u32 = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits
	    }
	}
	return u.f32;
}

//
// convert a float to a halffloat (16-bit float)
//
halffloat
__STX_float_to_halffloat(float f32) {
	uint16    hs, he, hm;
	uint32 x, xs, xe, xm;
	int hes;
	union {
	    uint32 u32;
	    float f32;
	} u;
	halffloat h;

	u.f32 = f32;
	x = u.u32;
	if( (x & 0x7FFFFFFFu) == 0 ) {  // Signed zero
	    h = (uint16) (x >> 16);  // Return the signed zero
	} else { // Not zero
	    xs = x & 0x80000000u;  // Pick off sign bit
	    xe = x & 0x7F800000u;  // Pick off exponent bits
	    xm = x & 0x007FFFFFu;  // Pick off mantissa bits
	    if( xe == 0 ) {  // Denormal will underflow, return a signed zero
		h = (uint16) (xs >> 16);
	    } else if( xe == 0x7F800000u ) {  // Inf or NaN (all the exponent bits are set)
		if( xm == 0 ) { // If mantissa is zero ...
		    h = (uint16) ((xs >> 16) | 0x7C00u); // Signed Inf
		} else {
		    h = (uint16) 0xFE00u; // NaN, only 1st mantissa bit set
		}
	    } else { // Normalized number
		hs = (uint16) (xs >> 16); // Sign bit
		hes = ((int)(xe >> 23)) - 127 + 15; // Exponent unbias the single, then bias the halfp
		if( hes >= 0x1F ) {  // Overflow
		    h = (uint16) ((xs >> 16) | 0x7C00u); // Signed Inf
		} else if( hes <= 0 ) {  // Underflow
		    if( (14 - hes) > 24 ) {  // Mantissa shifted all the way off & no rounding possibility
			hm = (uint16) 0u;  // Set mantissa to zero
		    } else {
			xm |= 0x00800000u;  // Add the hidden leading bit
			hm = (uint16) (xm >> (14 - hes)); // Mantissa
			if( (xm >> (13 - hes)) & 0x00000001u ) // Check for rounding
			    hm += (uint16) 1u; // Round, might overflow into exp bit, but this is OK
		    }
		    h = (hs | hm); // Combine sign bit and mantissa bits, biased exponent is zero
		} else {
		    he = (uint16) (hes << 10); // Exponent
		    hm = (uint16) (xm >> 13); // Mantissa
		    if( xm & 0x00001000u ) // Check for rounding
			h = (hs | he | hm) + (uint16) 1u; // Round, might overflow to inf, this is OK
		    else
			h = (hs | he | hm);  // No rounding
		}
	    }
	}
	return h;
}

%}
! !

!HalfFloatArray class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2014 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    HalfFloatArrays store half precision (16bit) floats (and nothing else).
    HalfFloats were traditionally seldom used, but seem to become more popular
    these days, as some 3D graphics accelerators and game engines use them
    for very dense and compact storage of texture and vertex data.

    Notice, that HalfFloats are not supported as first class objects by the ST/X system;
    i.e. outside of a HalfFloatArray, these values are represented as floats
    or doubles. When accessing a HalfFloatArray's element via getters/setters,
    shortFloat (i.e. single precision 32bit floats) are exchanged.

    Be aware that the numeric range of a half-float is very very limited.

    [memory requirements:]
        OBJ-HEADER + (size * 2)

    [See also:]
        FloatArray DoubleArray Array
        http://www.opengl.org/wiki/Small_Float_Formats

    [author:]
        Claus Gittinger
"
! !

!HalfFloatArray class methodsFor:'queries'!

elementByteSize
    "for bit-like containers, return the number of bytes stored per element.
     Here, 2 is returned"

    ^ 2
! !

!HalfFloatArray methodsFor:'accessing'!

at:index
%{  /* NOCONTEXT */
    if (__isSmallInteger(index)) {
	int i = __intVal(index);
	int n = __wordArraySize(self);

	if ((unsigned)i <= n) {
	    unsigned short h;
	    OBJ newFloat;
	    float f;

	    h = __WordArrayInstPtr(self)->s_element[i-1];

	    f = __STX_halffloat_to_float(h);
	    __qMKSFLOAT(newFloat, f);
	    RETURN ( newFloat );
	}
    }
%}.
    self primitiveFailed
!

at:index put:aFloat
%{
    if (__isSmallInteger(index)) {
	int i = __intVal(index);
	int n = __wordArraySize(self);

	if ((unsigned)i <= n) {
	    unsigned short h;
	    float f;

	    if (__isFloat(aFloat)) {
		f = (float)(__floatVal(aFloat));
	    } else if (__isShortFloat(aFloat)) {
		f = __shortFloatVal(aFloat);
	    } else if (__isSmallInteger(aFloat)) {
		f = (float)(__intVal(aFloat));
	    } else
		goto error;

	    h = __STX_float_to_halffloat(f);
	    __WordArrayInstPtr(self)->s_element[i-1] = h;
	    RETURN (aFloat);
	}
    }
  error: ;
%}.
    self primitiveFailed
! !

!HalfFloatArray methodsFor:'queries'!

defaultElement
    ^ ShortFloat zero
!

isValidElement:anObject
    "return true, if I can hold this kind of object"

    ^ anObject isNumber
!

numFloats
    ^ self size
! !

!HalfFloatArray methodsFor:'testing'!

isFloatArray
    "return true if the receiver has float elements.
     These are Float, Double- and HalfFloat arrays"

    ^ true

    "Created: / 02-03-2019 / 23:14:00 / Claus Gittinger"
! !

!HalfFloatArray class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !