--- /dev/null
+/* $OpenBSD: vecast.c,v 1.1 2022/10/22 17:50:28 gkoehler Exp $ */
+
+/*
+ * Copyright (c) 2022 George Koehler <gkoehler@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <altivec.h>
+#include <err.h>
+#include <math.h>
+#include <stdio.h>
+
+struct double4 {
+ double d[4];
+};
+
+union vu {
+ vector float vf;
+ vector int vi;
+ vector unsigned vu;
+ float f[4];
+ int i[4];
+ unsigned u[4];
+};
+
+#define AD(a, b, c, d) (struct double4){a, b, c, d}
+#define VF(a, b, c, d) (vector float)(a, b, c, d)
+#define VI(a, b, c, d) (vector int)(a, b, c, d)
+#define VU(a, b, c, d) (vector unsigned)(a, b, c, d)
+#define rsqrt(f) (1.0 / sqrt(f))
+
+int fail;
+
+void
+ck_equal(const char *what, vector float out, vector float answer)
+{
+ if (vec_any_ne(out, answer)) {
+ union vu a, b;
+
+ a.vf = out;
+ b.vf = answer;
+ warnx("%s: {%a, %a, %a, %a} should be {%a, %a, %a, %a}",
+ what, a.f[0], a.f[1], a.f[2], a.f[3],
+ b.f[0], b.f[1], b.f[2], b.f[3]);
+ fail = 1;
+ }
+}
+
+void
+ck_equal_i(const char *what, vector int out, vector int answer)
+{
+ if (vec_any_ne(out, answer)) {
+ union vu a, b;
+
+ a.vi = out;
+ b.vi = answer;
+ warnx("%s: {%d, %d, %d, %d} should be {%d, %d, %d, %d}",
+ what, a.i[0], a.i[1], a.i[2], a.i[3],
+ b.i[0], b.i[1], b.i[2], b.i[3]);
+ fail = 1;
+ }
+}
+
+void
+ck_equal_u(const char *what, vector unsigned out, vector unsigned answer)
+{
+ if (vec_any_ne(out, answer)) {
+ union vu a, b;
+
+ a.vi = out;
+ b.vi = answer;
+ warnx("%s: {%u, %u, %u, %u} should be {%u, %u, %u, %u}",
+ what, a.u[0], a.u[1], a.u[2], a.u[3],
+ b.u[0], b.u[1], b.u[2], b.u[3]);
+ fail = 1;
+ }
+}
+
+enum error_check {REL_1_IN, ABS_1_IN};
+
+/* Checks that error is at most 1 in err_den. */
+void
+ck_estimate(const char *what, vector float out, struct double4 answer,
+ enum error_check which, double err_den)
+{
+ union vu u;
+ int i, warned = 0;
+
+ u.vf = out;
+ for (i = 0; i < 4; i++) {
+ double estimate = u.f[i];
+ double target = answer.d[i];
+ double error;
+
+ switch (which) {
+ case REL_1_IN: /* relative error */
+ error = fabs(target / (estimate - target));
+ break;
+ case ABS_1_IN: /* absolute error */
+ error = fabs(1 / (estimate - target));
+ break;
+ default:
+ errx(1, "invalid check");
+ }
+
+ if (error < err_den) {
+ if (!warned) {
+ warnx("%s: {%a, %a, %a, %a} should be "
+ "near {%a, %a, %a, %a} (1/%g)",
+ what, u.f[0], u.f[1], u.f[2], u.f[3],
+ answer.d[0], answer.d[1], answer.d[2],
+ answer.d[3], err_den);
+ warned = 1;
+ fail = 1;
+ }
+ warnx("%a is off %a by 1/%g", estimate, target,
+ error);
+ }
+ }
+}
+
+/*
+ * Tries altivec with denormal or subnormal floats.
+ * These are single-precision floats f, where
+ * 0 < |f| < 2**-126 = 0x1p-126 = 0x10p-130 = 1.17549435E-38F
+ */
+int
+main(void)
+{
+ struct double4 dan;
+ volatile vector float in1, in2, in3;
+ vector float ans;
+ vector int ian;
+ vector unsigned uan;
+
+ /* in1 + in2 */
+ in1 = VF(10, 0x10p-140, 0x20p-130, -0x2000p-134);
+ in2 = VF( 4, 0x5p-140, -0x1p-130, 0x1fffp-134);
+ ans = VF(14, 0x15p-140, 0x1fp-130, -0x1p-134);
+ ck_equal("vec_add", vec_add(in1, in2), ans);
+
+ /* in1 - in2 */
+ in1 = VF(0x4000p-134, 10, 0x10p-140, 0x3p-130);
+ in2 = VF(0x3ffep-134, 4, 0x5p-140, 0x40p-130);
+ ans = VF( 0x2p-134, 6, 0xbp-140, -0x3dp-130);
+ ck_equal("vec_sub", vec_sub(in1, in2), ans);
+
+ /* in1 * in2 + in3 */
+ in1 = VF( 0x6p-70, 0x6p-140, 6, 0x6p-100);
+ in2 = VF( 0x7p-70, 0x7p50, 7, 0x7p-30);
+ in3 = VF( 0, 0, 1, -0x20p-130);
+ ans = VF(0x2ap-140, 0x2ap-90, 43, 0xap-130);
+ ck_equal("vec_madd", vec_madd(in1, in2, in3), ans);
+
+ /* in3 - in1 * in2 */
+ in1 = VF( 0xbp-30, 0xbp-70, 0xbp44, 11);
+ in2 = VF( 0x3p-100, 0x3p-70, -0x3p-138, 3);
+ in3 = VF(0x25p-130, 0, 0, 35);
+ ans = VF( 0x4p-130, -0x21p-140, 0x21p-94, 2);
+ ck_equal("vec_nmsub", vec_nmsub(in1, in2, in3), ans);
+
+ /* 1 / in1 */
+ in1 = VF( 3, 0x3p126, 0x3p-126, 0x1p127);
+ dan = AD(1.0 / 3, 1.0 / 0x3p126, 1.0 / 0x3p-126, 0x1p-127);
+ ck_estimate("vec_re", vec_re(in1), dan, REL_1_IN, 4096);
+
+ /* 1 / sqrt(in1) */
+ in1 = VF(1, 2, 0x1p-128, 0x5p-135);
+ dan = AD(1, rsqrt(2), rsqrt(0x1p-128), rsqrt(0x5p-135));
+ ck_estimate("vec_rsqrt", vec_rsqrte(in1), dan, REL_1_IN, 4096);
+
+ /* log2(in1) */
+ in1 = VF(0x1p-130, 0x1p-149, 32, 0x1p-10);
+ dan = AD( -130, -149, 5, -10);
+ ck_estimate("vec_loge", vec_loge(in1), dan, ABS_1_IN, 32);
+ in1 = VF( 0x123p-139, 0xabcp-145, 1, 1);
+ dan = AD(log2(0x123p-139), log2(0xabcp-145), 0, 0);
+ ck_estimate("vec_loge", vec_loge(in1), dan, ABS_1_IN, 32);
+
+ /* 2**in1 */
+ in1 = VF( -149, -138, -127, 10);
+ ans = VF(0x1p-149, 0x1p-138, 0x1p-127, 1024);
+ ck_equal("vec_expte", vec_expte(in1), ans);
+ in1 = VF( -10, -145.3, -136.9, -127.1);
+ dan = AD(0x1p-10, exp2(-145.3), exp2(-136.9), exp2(-127.1));
+ ck_estimate("vec_expte", vec_expte(in1), dan, REL_1_IN, 16);
+
+ /* (int)(in1 * 2**exponent) */
+ in1 = VF(0x1p-127, 2.34, -0xfedp-140, -19.8);
+ ian = VI( 0, 2, 0, -19);
+ ck_equal_i("vec_cts", vec_cts(in1, 0), ian);
+ in1 = VF(0x1p-113, -1, -0xabcp-143, 0x1fp-10);
+ ian = VI( 0, -1024, 0, 0x1f);
+ ck_equal_i("vec_cts", vec_cts(in1, 10), ian);
+
+ /* (unsigned)(in1 * 2**exponent) */
+ in1 = VF(0x1.ap-130, 0x1.ep-140, 24000012, 0);
+ uan = VU( 0, 0, 3072001536, 0);
+ ck_equal_u("vec_ctu", vec_ctu(in1, 7), uan);
+
+ return fail;
+}