{"id":16831185,"url":"https://github.com/zbjornson/tacvt","last_synced_at":"2026-04-16T17:34:27.869Z","repository":{"id":85612761,"uuid":"169926674","full_name":"zbjornson/tacvt","owner":"zbjornson","description":"Fast conversion from one type of TypedArray to another","archived":false,"fork":false,"pushed_at":"2019-02-19T00:02:10.000Z","size":36,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-24T09:12:10.176Z","etag":null,"topics":["ecmascript","javascript","simd","typedarrays"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zbjornson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-09T23:56:21.000Z","updated_at":"2019-02-19T00:02:11.000Z","dependencies_parsed_at":"2023-06-06T08:15:37.325Z","dependency_job_id":null,"html_url":"https://github.com/zbjornson/tacvt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zbjornson%2Ftacvt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zbjornson%2Ftacvt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zbjornson%2Ftacvt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zbjornson%2Ftacvt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zbjornson","download_url":"https://codeload.github.com/zbjornson/tacvt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244135905,"owners_count":20403797,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecmascript","javascript","simd","typedarrays"],"created_at":"2024-10-13T11:42:37.779Z","updated_at":"2025-10-18T22:23:44.520Z","avatar_url":"https://github.com/zbjornson.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"Quick experiment regarding fast initialization of one TypedArray from another of\na different type, e.g.\n\n```js\nconst inp = new Float32Array(len);\nconst out = new Uint16Array(inp);\n```\n\nor\n\n```js\nconst inp = new Int8Array(len);\nconst out = new Float32Array(len);\nout.set(inp);\n```\n\n### How\n\nv8 does scalar conversion with a generic conversion routine. This module uses\n256-bit-wide SIMD conversions and has specialized conversion routines.\n\n[ECMAScript's conversion routines](https://tc39.github.io/ecma262/#sec-touint32)\ndon't all match well with Intel's instructions and some have to be implemented\nin software. (Keep that in mind if you're doing something that needs fast\nconversions and don't need to adhere to the ECMAScript rules; see for example\nthe note in `Float32Array` to `Int32Array` below.)\n\n### Coverage\n\n* **Float/double conversions** are correct and fast (Intel ins'n match ECMA spec).  \n  ✔️ `Float64Array` to `Float32Array`  \n  ✔️ `Float32Array` to `Float64Array`\n\n* **Float to integer conversions** require specializations; only one is done.  \n  ✔️ `Float32Array` to `Int32Array`\n  ✔️ `Float64Array` to `Int32Array` are correct and fairly fast. (Depending on\n  the values, *much* faster than v8.) Intel's instructions don't match\n  ECMA262 exactly: ECMA262 specifies that NaN, +Infinity and -Infinity to return\n  0, and that values wrap-around in case of overflow, whereas Intel's\n  `cvt[t]ps2dq` returns 0x80000000 (-2147483648) in these cases. (Also,\n  ECMA262's `ToInt32` does not match the behavior of `static_cast\u003cint32_t\u003e` in\n  C++.) Right now this module has a fast path for when the instruction matches\n  the spec (better than v8's fast path, see *TODO* below), and a slow scalar\n  path to fix up values that don't.\n\n  AVX512 `vfixupimmps` is potentially useful here but not widely available.\n\n  I have no use case for this conversion, but if someone else does, would it be\n  useful to offer fast conversion that doesn't follow ECMA262 spec and instead\n  just passes through Intel's instruction behavior?\n\n  **TODO** I think there's a missed optimization in v8's DoubleToInt32.\n  Their fast-path requires this condition:\n  ```cpp\n  static_cast\u003cdouble\u003e(static_cast\u003cint32_t\u003e(double_input)) == double_input\n  ```\n  but I think it should be\n  ```cpp\n  static_cast\u003cdouble\u003e(static_cast\u003cint32_t\u003e(double_input)) == trunc(double_input)\n  ```\n  ❌ `Float32Array` to `Uint32Array` (AVX512)  \n  ❌ `Float32Array` to `Int16Array` (SSE 4-at-a-time) \n  ❌ `Float32Array` to `Uint16Array`  \n  ❌ `Float32Array` to `Int8Array` (SSE 4-at-a-time)  \n  ❌ `Float32Array` to `Uint8Array`  \n  ❌ `Float64Array` to `Uint32Array`  \n  ❌ `Float64Array` to `Int16Array`  \n  ❌ `Float64Array` to `Uint16Array`  \n  ❌ `Float64Array` to `Int8Array`  \n  ❌ `Float64Array` to `Uint8Array` require in-software specializations\n\n* **Integer to float conversions** are correct and fast, with two exceptions.  \n  ✔️ `Int32Array` to `Float64Array`  \n  ✔️ `Int32Array` to `Float32Array`  \n  ✔️ `Int16Array` to `Float32Array`  \n  ✔️ `Uint16Array` to `Float32Array`  \n  ✔️ `Int8Array` to `Float32Array`  \n  ✔️ `Uint8Array` to `Float32Array`  \n  ❌ `Uint32Array` to `Float32Array` and  \n  ❌ `Uint32Array` to `Float64Array` require either AVX512 or in-software specializations\n\n* **Widening integer conversions** are correct and fast.  \n  ✔️ ️`Int16Array` to `Int32Array`  \n  ✔️ ️`Int16Array` to `Uint32Array`  \n  ✔️ ️`Uint16Array` to `Int32Array`  \n  ✔️ ️`Uint16Array` to `Uint32Array`  \n  ✔️ ️`Int8Array` to `Int32Array`  \n  ✔️ ️`Int8Array` to `Uint32Array`  \n  ✔️ ️`Int8Array` to `Int16Array`  \n  ✔️ ️`Int8Array` to `Uint16Array`  \n  ✔️ ️`Uint8Array` to `Int32Array`  \n  ✔️ ️`Uint8Array` to `Uint32Array`  \n  ✔️ ️`Uint8Array` to `Int16Array`  \n  ✔️ ️`Uint8Array` to `Uint16Array`\n\n* **Unsigned/signed conversions** are just `memcpy()`s (reinterpretations of the\n  same bit strings). v8 is fast; this module passes-through to `dst.set(src)`.  \n  ✔️ `Int32Array` to `Uint32Array`  \n  ✔️ `Uint32Array` to `Int32Array`  \n  ✔️ `Int16Array` to `Uint16Array`  \n  ✔️ `Uint16Array` to `Int16Array`  \n  ✔️ `Int8Array` to `Uint8Array`  \n  ✔️ `Uint8Array` to `Int8Array`\n\n* **Narrowing integer conversions** are correct and fast.  \n  ✔️ `Int32Array` to `Int16Array`  \n  ✔️ `Int32Array` to `Int8Array`  \n  ✔️ `Uint32Array` to `Int16Array`  \n  ✔️ `Uint32Array` to `Uint16Array`  \n  ✔️ `Uint32Array` to `Int8Array`  \n  ✔️ `Uint32Array` to `Uint8Array`  \n  ✔️ `Int16Array` to `Int8Array`  \n  ✔️ `Int16Array` to `Uint8Array`  \n  ✔️ `Uint16Array` to `Int8Array`  \n  ✔️ `Uint16Array` to `Uint8Array`\n\nConversions that aren't implemented pass-through to `dst.set(src)`.\n\n### Performance\n\nRun `node ./test.js --benchmark`.\n\nNumbers are `dst.set(src)` (v8) ÷ `set(dst, src)` (this module).\nThe diagonal should be 1 or slightly less than 1; deviation from 1 there can estimate the noise in the benchmark.\nI've marked ones that are actually expected to be faster with asterisks below.\n\n```\nLinux/GCC8\n┌──────────────┬──────────────┬──────────────┬────────────┬─────────────┬────────────┬─────────────┬───────────┬────────────┐\n│ from   \\  to │ Float64Array │ Float32Array │ Int32Array │ Uint32Array │ Int16Array │ Uint16Array │ Int8Array │ Uint8Array │\n├──────────────┼──────────────┼──────────────┼────────────┼─────────────┼────────────┼─────────────┼───────────┼────────────┤\n│ Float64Array │     0.85     │    *4.19*    │   *6.05*   │    0.97     │    1.01    │    0.98     │   0.96    │    1.02    │\n│ Float32Array │    *4.46*    │     1.06     │  *22.63*   │    1.02     │    0.99    │    0.99     │   1.00    │    1.01    │\n│   Int32Array │    *4.43*    │    *7.18*    │    1.06    │    1.06     │  *13.71*   │  *14.65*    │ *10.57*   │   *7.19*   │\n│  Uint32Array │     1.10     │     0.93     │    1.48    │    0.99     │  *11.53*   │  *10.56*    │ *12.11*   │  *11.95*   │\n│   Int16Array │    *5.76*    │    *5.94*    │   *9.67*   │  *10.84*    │    0.96    │    1.00     │ *21.12*   │  *16.02*   │\n│  Uint16Array │    *4.72*    │    *9.93*    │  *10.60*   │  *12.06*    │    1.02    │    1.05     │ *18.54*   │  *15.09*   │\n│    Int8Array │    *2.77*    │   *12.96*    │  *11.74*   │  *10.85*    │  *25.11*   │  *21.40*    │   1.05    │    0.75    │\n│   Uint8Array │    *6.38*    │   *10.49*    │  *12.32*   │   *9.86*    │  *20.77*   │  *16.01*    │   0.88    │    0.90    │\n└──────────────┴──────────────┴──────────────┴────────────┴─────────────┴────────────┴─────────────┴───────────┴────────────┘\n```\n\n```\nWindows/MSVS 2017\n┌──────────────┬──────────────┬──────────────┬────────────┬─────────────┬────────────┬─────────────┬───────────┬────────────┐\n│ from   \\  to │ Float64Array │ Float32Array │ Int32Array │ Uint32Array │ Int16Array │ Uint16Array │ Int8Array │ Uint8Array │\n├──────────────┼──────────────┼──────────────┼────────────┼─────────────┼────────────┼─────────────┼───────────┼────────────┤\n│ Float64Array │     1.04     │    *4.64*    │   *9.21*   │    1.02     │    1.08    │    0.92     │   0.95    │    0.96    │\n│ Float32Array │    *4.16*    │     1.09     │  *35.19*   │    1.00     │    1.04    │    0.94     │   1.01    │    1.03    │\n│   Int32Array │    *4.49*    │    *6.91*    │    1.05    │    0.98     │   *8.57*   │  *11.02*    │  *9.43*   │   *9.26*   │\n│  Uint32Array │     0.98     │     1.25     │    1.02    │    0.98     │   *7.32*   │   *8.28*    │  *8.45*   │   *5.30*   │\n│   Int16Array │    *3.68*    │    *9.44*    │   *8.28*   │   *8.42*    │    0.95    │    0.91     │  *8.94*   │  *13.77*   │\n│  Uint16Array |    *5.18*    │    *7.81*    │   *9.03*   │   *7.80*    │    1.02    │    0.80     │ *16.33*   │   *9.17*   │\n│    Int8Array │    *6.21*    │    *9.95*    │   *8.10*   │   *7.27*    │  *14.54*   │   *9.84*    │   0.97    │    1.02    │\n│   Uint8Array │    *3.82*    │    *9.61*    │   *9.46*   │   *9.41*    │  *14.75*   │  *14.69*    │   1.01    │    0.91    │\n└──────────────┴──────────────┴──────────────┴────────────┴─────────────┴────────────┴─────────────┴───────────┴────────────┘\n```\n\nNote: Float32Array to Int32Array benchmark has almost no cases of overflow or\nother fixup. Actual runtime depends on numerical values in array.\n\n### Other TODOs\n\n* The `offset` parameter is ignored.\n* The source/destination must have a length that is a multiple of 8, 16 or 32.\n  (That is, I've only dealt with the vectorized loop body and not the tail.)\n* AVX2 is required, and most or all of these could be done with earlier\n  extension sets albeit with narrower vectors. Since this library is just for\n  fun, I have no intention of adding e.g. an SSE4.2 version.\n\n### Why\n\nWas a fun weekend project. I have no idea if anyone ever uses these conversions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzbjornson%2Ftacvt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzbjornson%2Ftacvt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzbjornson%2Ftacvt/lists"}