]> git.kaiwu.me - quickjs.git/commitdiff
added Float16Array (bnoordhuis) - optimized float16 conversion functions
authorFabrice Bellard <fabrice@bellard.org>
Mon, 5 May 2025 17:12:32 +0000 (19:12 +0200)
committerFabrice Bellard <fabrice@bellard.org>
Mon, 5 May 2025 17:12:32 +0000 (19:12 +0200)
TODO
cutils.h
quickjs-atom.h
quickjs.c
quickjs.h
test262.conf
test262_errors.txt
tests/test_bjson.js
tests/test_builtin.js

diff --git a/TODO b/TODO
index db4ff5f557acde33c9f15bc6d7a4ff0cf147746a..4e2dfb904a1eb2ddf5e97e401ef9051e760df824 100644 (file)
--- a/TODO
+++ b/TODO
@@ -62,6 +62,5 @@ Optimization ideas:
 Test262o:   0/11262 errors, 463 excluded
 Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
 
-Result: 65/78182 errors, 1628 excluded, 7233 skipped
+Result: 64/78320 errors, 1624 excluded, 7166 skipped
 Test262 commit: 27622d764767dcb3778784884022c2c7de5769b8
-
index 32b97579db695e8873e3a3b69060c60c8e1c7dd5..9fcb7a60794e5ad099b15353c6bde2f958328027 100644 (file)
--- a/cutils.h
+++ b/cutils.h
@@ -364,4 +364,60 @@ static inline double uint64_as_float64(uint64_t u64)
     return u.d;
 }
 
+static inline double fromfp16(uint16_t v)
+{
+    double d;
+    uint32_t v1;
+    v1 = v & 0x7fff;
+    if (unlikely(v1 >= 0x7c00))
+        v1 += 0x1f8000; /* NaN or infinity */
+    d = uint64_as_float64(((uint64_t)(v >> 15) << 63) | ((uint64_t)v1 << (52 - 10)));
+    return d * 0x1p1008;
+}
+
+static inline uint16_t tofp16(double d)
+{
+    uint64_t a, addend;
+    uint32_t v, sgn;
+    int shift;
+    
+    a = float64_as_uint64(d);
+    sgn = a >> 63;
+    a = a & 0x7fffffffffffffff;
+    if (unlikely(a > 0x7ff0000000000000)) {
+        /* nan */
+        v = 0x7c01;
+    } else if (a < 0x3f10000000000000) { /* 0x1p-14 */
+        /* subnormal f16 number or zero */
+        if (a <= 0x3e60000000000000) { /* 0x1p-25 */
+            v = 0x0000; /* zero */
+        } else {
+            shift = 1051 - (a >> 52);
+            a = ((uint64_t)1 << 52) | (a & (((uint64_t)1 << 52) - 1));
+            addend = ((a >> shift) & 1) + (((uint64_t)1 << (shift - 1)) - 1);
+            v = (a + addend) >> shift;
+        }
+    } else {
+        /* normal number or infinity */
+        a -= 0x3f00000000000000; /* adjust the exponent */
+        /* round */
+        addend = ((a >> (52 - 10)) & 1) + (((uint64_t)1 << (52 - 11)) - 1);
+        v = (a + addend) >> (52 - 10);
+        /* overflow ? */
+        if (unlikely(v > 0x7c00))
+            v = 0x7c00;
+    }
+    return v | (sgn << 15);
+}
+
+static inline int isfp16nan(uint16_t v)
+{
+    return (v & 0x7FFF) > 0x7C00;
+}
+
+static inline int isfp16zero(uint16_t v)
+{
+    return (v & 0x7FFF) == 0;
+}
+
 #endif  /* CUTILS_H */
index 73766f23c7f11414d3d2f7797832d11f3b6ed996..5e46d1b245c22f26f883e52b62e97e9b020e14b6 100644 (file)
@@ -211,6 +211,7 @@ DEF(Int32Array, "Int32Array")
 DEF(Uint32Array, "Uint32Array")
 DEF(BigInt64Array, "BigInt64Array")
 DEF(BigUint64Array, "BigUint64Array")
+DEF(Float16Array, "Float16Array")
 DEF(Float32Array, "Float32Array")
 DEF(Float64Array, "Float64Array")
 DEF(DataView, "DataView")
index 5d61957dff1f4aace98d0b1c9ce154be9726524e..611997584901fa4cd10d0d8a2d9794ea5705a431 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -147,6 +147,7 @@ enum {
     JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */
     JS_CLASS_BIG_INT64_ARRAY,   /* u.array (typed_array) */
     JS_CLASS_BIG_UINT64_ARRAY,  /* u.array (typed_array) */
+    JS_CLASS_FLOAT16_ARRAY,     /* u.array (typed_array) */
     JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */
     JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */
     JS_CLASS_DATAVIEW,          /* u.typed_array */
@@ -974,6 +975,7 @@ struct JSObject {
                 uint32_t *uint32_ptr;   /* JS_CLASS_UINT32_ARRAY */
                 int64_t *int64_ptr;     /* JS_CLASS_INT64_ARRAY */
                 uint64_t *uint64_ptr;   /* JS_CLASS_UINT64_ARRAY */
+                uint16_t *fp16_ptr;     /* JS_CLASS_FLOAT16_ARRAY */
                 float *float_ptr;       /* JS_CLASS_FLOAT32_ARRAY */
                 double *double_ptr;     /* JS_CLASS_FLOAT64_ARRAY */
             } u;
@@ -1502,6 +1504,7 @@ static JSClassShortDef const js_std_class_def[] = {
     { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT32_ARRAY */
     { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark },   /* JS_CLASS_BIG_INT64_ARRAY */
     { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark },  /* JS_CLASS_BIG_UINT64_ARRAY */
+    { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT16_ARRAY */
     { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT32_ARRAY */
     { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */
     { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */
@@ -5091,6 +5094,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
     case JS_CLASS_UINT32_ARRAY:
     case JS_CLASS_BIG_INT64_ARRAY:
     case JS_CLASS_BIG_UINT64_ARRAY:
+    case JS_CLASS_FLOAT16_ARRAY:
     case JS_CLASS_FLOAT32_ARRAY:
     case JS_CLASS_FLOAT64_ARRAY:
         p->is_exotic = 1;
@@ -6482,6 +6486,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
         case JS_CLASS_UINT32_ARRAY:      /* u.typed_array / u.array */
         case JS_CLASS_BIG_INT64_ARRAY:   /* u.typed_array / u.array */
         case JS_CLASS_BIG_UINT64_ARRAY:  /* u.typed_array / u.array */
+        case JS_CLASS_FLOAT16_ARRAY:     /* u.typed_array / u.array */
         case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */
         case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */
         case JS_CLASS_DATAVIEW:          /* u.typed_array */
@@ -8344,6 +8349,9 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
         case JS_CLASS_BIG_UINT64_ARRAY:
             if (unlikely(idx >= p->u.array.count)) goto slow_path;
             return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
+        case JS_CLASS_FLOAT16_ARRAY:
+            if (unlikely(idx >= p->u.array.count)) goto slow_path;
+            return __JS_NewFloat64(ctx, fromfp16(p->u.array.u.fp16_ptr[idx]));
         case JS_CLASS_FLOAT32_ARRAY:
             if (unlikely(idx >= p->u.array.count)) goto slow_path;
             return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
@@ -9178,6 +9186,13 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
                 p->u.array.u.uint64_ptr[idx] = v;
             }
             break;
+        case JS_CLASS_FLOAT16_ARRAY:
+            if (JS_ToFloat64Free(ctx, &d, val))
+                return -1;
+            if (unlikely(idx >= (uint32_t)p->u.array.count))
+                goto ta_out_of_bound;
+            p->u.array.u.fp16_ptr[idx] = tofp16(d);
+            break;
         case JS_CLASS_FLOAT32_ARRAY:
             if (JS_ToFloat64Free(ctx, &d, val))
                 return -1;
@@ -13240,6 +13255,9 @@ static void js_print_object(JSPrintValueState *s, JSObject *p)
             case JS_CLASS_BIG_UINT64_ARRAY:
                 js_printf(s, "%" PRIu64, *(uint64_t *)ptr);
                 break;
+            case JS_CLASS_FLOAT16_ARRAY:
+                js_print_float64(s, fromfp16(*(uint16_t *)ptr));
+                break;
             case JS_CLASS_FLOAT32_ARRAY:
                 js_print_float64(s, *(float *)ptr);
                 break;
@@ -43888,6 +43906,11 @@ static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
     return JS_NewFloat64(ctx, r);
 }
 
+static double js_math_f16round(double a)
+{
+    return fromfp16(tofp16(a));
+}
+
 static double js_math_fround(double a)
 {
     return (float)a;
@@ -43991,6 +44014,7 @@ static const JSCFunctionListEntry js_math_funcs[] = {
     JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
     JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
     JS_CFUNC_DEF("random", 0, js_math_random ),
+    JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ),
     JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
     JS_CFUNC_DEF("imul", 2, js_math_imul ),
     JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
@@ -51958,8 +51982,8 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
 
 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
     0, 0, 0, 1, 1, 2, 2,
-    3, 3, /* BigInt64Array, BigUint64Array */
-    2, 3
+    3, 3,                   // BigInt64Array, BigUint64Array
+    1, 2, 3                 // Float16Array, Float32Array, Float64Array
 };
 
 static JSValue js_array_buffer_constructor3(JSContext *ctx,
@@ -52892,7 +52916,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
         double d;
         if (JS_ToFloat64(ctx, &d, argv[0]))
             return JS_EXCEPTION;
-        if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
+        if (p->class_id == JS_CLASS_FLOAT16_ARRAY) {
+            v64 = tofp16(d);
+        } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
             union {
                 float f;
                 uint32_t u32;
@@ -53023,6 +53049,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
     int64_t v64;
     double d;
     float f;
+    uint16_t hf;
 
     len = js_typed_array_get_length_internal(ctx, this_val);
     if (len < 0)
@@ -53185,6 +53212,39 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
             }
         }
         break;
+    case JS_CLASS_FLOAT16_ARRAY:
+        if (is_bigint)
+            break;
+        if (isnan(d)) {
+            const uint16_t *pv = p->u.array.u.fp16_ptr;
+            /* special case: indexOf returns -1, includes finds NaN */
+            if (special != special_includes)
+                goto done;
+            for (; k != stop; k += inc) {
+                if (isfp16nan(pv[k])) {
+                    res = k;
+                    break;
+                }
+            }
+        } else if (d == 0) {
+            // special case: includes also finds negative zero
+            const uint16_t *pv = p->u.array.u.fp16_ptr;
+            for (; k != stop; k += inc) {
+                if (isfp16zero(pv[k])) {
+                    res = k;
+                    break;
+                }
+            }
+        } else if (hf = tofp16(d), d == fromfp16(hf)) {
+            const uint16_t *pv = p->u.array.u.fp16_ptr;
+            for (; k != stop; k += inc) {
+                if (pv[k] == hf) {
+                    res = k;
+                    break;
+                }
+            }
+        }
+        break;
     case JS_CLASS_FLOAT32_ARRAY:
         if (is_bigint)
             break;
@@ -53575,6 +53635,11 @@ static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
     return (y < x) - (y > x);
 }
 
+static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) {
+    return js_cmp_doubles(fromfp16(*(const uint16_t *)a),
+                          fromfp16(*(const uint16_t *)b));
+}
+
 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
     return js_cmp_doubles(*(const float *)a, *(const float *)b);
 }
@@ -53615,6 +53680,10 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
     return JS_NewBigUint64(ctx, *(uint64_t *)a);
 }
 
+static JSValue js_TA_get_float16(JSContext *ctx, const void *a) {
+    return __JS_NewFloat64(ctx, fromfp16(*(const uint16_t *)a));
+}
+
 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
     return __JS_NewFloat64(ctx, *(const float *)a);
 }
@@ -53740,6 +53809,10 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
             tsc.getfun = js_TA_get_uint64;
             cmpfun = js_TA_cmp_uint64;
             break;
+        case JS_CLASS_FLOAT16_ARRAY:
+            tsc.getfun = js_TA_get_float16;
+            cmpfun = js_TA_cmp_float16;
+            break;
         case JS_CLASS_FLOAT32_ARRAY:
             tsc.getfun = js_TA_get_float32;
             cmpfun = js_TA_cmp_float32;
@@ -54295,6 +54368,14 @@ static JSValue js_dataview_getValue(JSContext *ctx,
             return JS_NewBigUint64(ctx, v);
         }
         break;
+    case JS_CLASS_FLOAT16_ARRAY:
+        {
+            uint16_t v;
+            v = get_u16(ptr);
+            if (is_swap)
+                v = bswap16(v);
+            return __JS_NewFloat64(ctx, fromfp16(v));
+        }
     case JS_CLASS_FLOAT32_ARRAY:
         {
             union {
@@ -54356,7 +54437,9 @@ static JSValue js_dataview_setValue(JSContext *ctx,
         double d;
         if (JS_ToFloat64(ctx, &d, val))
             return JS_EXCEPTION;
-        if (class_id == JS_CLASS_FLOAT32_ARRAY) {
+        if (class_id == JS_CLASS_FLOAT16_ARRAY) {
+            v = tofp16(d);
+        } else if (class_id == JS_CLASS_FLOAT32_ARRAY) {
             union {
                 float f;
                 uint32_t i;
@@ -54385,6 +54468,7 @@ static JSValue js_dataview_setValue(JSContext *ctx,
         break;
     case JS_CLASS_INT16_ARRAY:
     case JS_CLASS_UINT16_ARRAY:
+    case JS_CLASS_FLOAT16_ARRAY:
         if (is_swap)
             v = bswap16(v);
         put_u16(ptr, v);
@@ -54421,6 +54505,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
     JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
     JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
     JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
+    JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ),
     JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
     JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
     JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
@@ -54431,6 +54516,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
     JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
     JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
     JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
+    JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ),
     JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
     JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
index 53425a25892d983c5c8d8d13967a3736a87cd7cd..45f9c080898b28a27daea9424bcae7dfe7919c67 100644 (file)
--- a/quickjs.h
+++ b/quickjs.h
@@ -871,6 +871,7 @@ typedef enum JSTypedArrayEnum {
     JS_TYPED_ARRAY_UINT32,
     JS_TYPED_ARRAY_BIG_INT64,
     JS_TYPED_ARRAY_BIG_UINT64,
+    JS_TYPED_ARRAY_FLOAT16,
     JS_TYPED_ARRAY_FLOAT32,
     JS_TYPED_ARRAY_FLOAT64,
 } JSTypedArrayEnum;
index 41168047764b90cb7ad29924b558127d49e9b856..2781015b18aa29722ebf789832fc361835baa5a2 100644 (file)
@@ -108,7 +108,7 @@ explicit-resource-management=skip
 exponentiation
 export-star-as-namespace-from-module
 FinalizationRegistry
-Float16Array=skip
+Float16Array
 Float32Array
 Float64Array
 for-in-order
@@ -344,12 +344,6 @@ test262/test/staging/sm/JSON/parse-with-source.js
 test262/test/staging/sm/RegExp/flags.js
 test262/test/staging/sm/RegExp/prototype.js
 
-# no f16
-test262/test/staging/sm/Math/f16round.js
-test262/test/staging/sm/TypedArray/sort_small.js
-test262/test/staging/sm/extensions/dataview.js
-test262/test/staging/sm/TypedArray/toString.js
-
 # not standard
 test262/test/staging/sm/Function/builtin-no-construct.js
 test262/test/staging/sm/Function/function-caller-restrictions.js
index 5fb832d6bae0ab4dd4227016bfe5c75ba4c8a9c4..6ce51a99938c87a62f8a17164f9dde4a1f7a8561 100644 (file)
@@ -28,7 +28,6 @@ test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:73: Error: Ass
 test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: Test262Error: Expected SameValue(«2», «6») to be true
 test262/test/staging/sm/TypedArray/set-detached-bigint.js:27: Error: Assertion failed: expected exception SyntaxError, got RangeError: invalid array length
 test262/test/staging/sm/TypedArray/set-detached.js:112: RangeError: invalid array length
-test262/test/staging/sm/TypedArray/sort-negative-nan.js:102: TypeError: cannot read property 'name' of undefined
 test262/test/staging/sm/TypedArray/sort_modifications.js:12: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
 test262/test/staging/sm/TypedArray/subarray.js:15: Test262Error: Expected SameValue(«0», «1») to be true
 test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:45: Error: Assertion failed: expected exception SyntaxError, no exception thrown
index a270796404fa32d2f08f1663df129791f8b24d16..a90bfe18dd5fc91813cb1cd395b90d79a40d59fe 100644 (file)
@@ -42,6 +42,7 @@ function isArrayLike(a)
         (a instanceof Int8Array) ||
         (a instanceof Int16Array) ||
         (a instanceof Int32Array) ||
+        (a instanceof Float16Array) ||
         (a instanceof Float32Array) ||
         (a instanceof Float64Array);
 }
@@ -157,6 +158,7 @@ function bjson_test_all()
     bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]);
 
     bjson_test(new Int32Array([123123, 222111, -32222]));
+    bjson_test(new Float16Array([1024, 1024.5]));
     bjson_test(new Float64Array([123123, 222111.5]));
 
     /* tested with a circular reference */
index 423841d33caf411b6d337ca6844889ce48b1fd06..667650a992d082604ba82da96fe1833f6f555713 100644 (file)
@@ -489,6 +489,9 @@ function test_typed_array()
     a = new Uint16Array(buffer, 2);
     a[0] = -1;
 
+    a = new Float16Array(buffer, 8, 1);
+    a[0] = 1;
+
     a = new Float32Array(buffer, 8, 1);
     a[0] = 1;