HalfFloatArray.st
author Claus Gittinger <cg@exept.de>
Wed, 08 May 2019 14:41:45 +0200
changeset 4942 9f424bed67c4
parent 4889 7d32114f8359
child 4980 bef95bf81d84
permissions -rw-r--r--
#DOCUMENTATION by cg class: Socket comment/format in: #bindAnonymously #bindTo:reuseAddress:

"
 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;
#if 0
#if defined(__GNUC__) || defined(__CLANG__) || defined(__MINGW__)
inline float 
XMConvertHalfToFloat(HALF Value) {
    __m128i V1 = _mm_cvtsi32_si128( static_cast<uint32_t>(Value) );
    __m128 V2 = _mm_cvtph_ps( V1 );
    return _mm_cvtss_f32( V2 );
}

inline HALF 
XMConvertFloatToHalf(float Value) {
    __m128 V1 = _mm_set_ss( Value );
    __m128i V2 = _mm_cvtps_ph( V1, 0 );
    return static_cast<HALF>( _mm_cvtsi128_si32(V2) );
}
#endif
#endif
//
// 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$'
! !