diff --git a/tests/blas/include/test.h b/tests/blas/include/test.h index ca2999b..deacce2 100644 --- a/tests/blas/include/test.h +++ b/tests/blas/include/test.h @@ -24,6 +24,22 @@ MunitResult test_saxpy_neg_stride(const MunitParameter params[], void* user_data_or_fixture); MunitResult test_saxpy_rounding(const MunitParameter params[], void* user_data_or_fixture); +MunitResult test_sasum_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_sdot_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_snrm2_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_saxpy_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_scopy_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_sscal_zero(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_saxpy_rounding_modes(const MunitParameter params[], + void* user_data_or_fixture); +MunitResult test_qasum_layout(const MunitParameter params[], + void* user_data_or_fixture); MunitResult test_scopy_all(const MunitParameter params[], void* user_data_or_fixture); MunitResult test_scopy_stride(const MunitParameter params[], diff --git a/tests/blas/level1/test_qasum.c b/tests/blas/level1/test_qasum.c index fe2cd13..ea90b3e 100644 --- a/tests/blas/level1/test_qasum.c +++ b/tests/blas/level1/test_qasum.c @@ -84,3 +84,19 @@ MunitResult test_qasum_negpi(const MunitParameter params[], free(QX); return MUNIT_OK; } + +// Pins the float128_t storage layout every q* test depends on: the sign and +// exponent live in the high word v[1]; v[0] is the low mantissa word. +MunitResult test_qasum_layout(const MunitParameter params[], + void* user_data_or_fixture) { + // 1.0 in IEEE binary128, written {lo, hi}. + float128_t one = {{ 0x0000000000000000, 0x3fff000000000000 }}; + assert_ullong(one.v[0], ==, 0x0000000000000000); // low mantissa word + assert_ullong(one.v[1], ==, 0x3fff000000000000); // sign+exponent word + + // A routine that reads the pair agrees: |1.0| == 1.0, bit-for-bit. + float128_t r = qasum(1, &one, 1, 'n'); + assert_ullong(r.v[1], ==, 0x3fff000000000000); + assert_ullong(r.v[0], ==, 0x0000000000000000); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_sasum.c b/tests/blas/level1/test_sasum.c index 7f35701..1e388cb 100644 --- a/tests/blas/level1/test_sasum.c +++ b/tests/blas/level1/test_sasum.c @@ -43,3 +43,13 @@ MunitResult test_sasum_stride(const MunitParameter params[], return MUNIT_OK; } + +// N==0 is a no-op: returns 0, reads nothing. +MunitResult test_sasum_zero(const MunitParameter params[], + void* user_data_or_fixture) { + float32_t* SX = svec((float[]){42.0f}, 1); + float32_t r = sasum(0, SX, 1, 'n'); + assert_ulong(r.v, ==, (uint32_t)SB_REAL32_ZERO); + free(SX); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_saxpy.c b/tests/blas/level1/test_saxpy.c index 7024844..a5ad0d1 100644 --- a/tests/blas/level1/test_saxpy.c +++ b/tests/blas/level1/test_saxpy.c @@ -116,3 +116,52 @@ MunitResult test_saxpy_rounding(const MunitParameter params[], free(SXu); free(SYu); free(SXn); free(SYn); return MUNIT_OK; } + +// Exercises all five rounding modes on a positive and a negative half-ULP +// tie (1.0 +/- 2^-24 via saxpy). Distinguishes every mode: round-up vs +// round-down differ by sign, round-toward-zero vs round-down differ on the +// negative operand, and 'a' (ties-away) rounds outward on both ties while +// 'n' (ties-even) stays (1.0 has an even mantissa). +MunitResult test_saxpy_rounding_modes(const MunitParameter params[], + void* user_data_or_fixture) { + const float32_t SA = { SB_REAL32_ONE }; + const float eps = 5.9604644775390625e-8f; // 2^-24 + const uint32_t ONE = 0x3f800000, UP = 0x3f800001; // 1.0, next-above + const uint32_t NEG_ONE = 0xbf800000, NEG_UP = 0xbf800001; // -1.0, next-below + + // positive tie: 1.0 + 2^-24 + struct { char m; uint32_t want; } pos[] = { + {'n', ONE}, {'z', ONE}, {'d', ONE}, {'u', UP}, {'a', UP} }; + for (uint64_t k = 0; k < 5; k++) { + float32_t* X = svec((float[]){eps}, 1); + float32_t* Y = svec((float[]){1.0f}, 1); + saxpy(1, SA, X, 1, Y, 1, pos[k].m); + assert_ulong(Y[0].v, ==, pos[k].want); + free(X); free(Y); + } + + // negative tie: -1.0 - 2^-24 + struct { char m; uint32_t want; } neg[] = { + {'n', NEG_ONE}, {'z', NEG_ONE}, {'u', NEG_ONE}, {'d', NEG_UP}, {'a', NEG_UP} }; + for (uint64_t k = 0; k < 5; k++) { + float32_t* X = svec((float[]){-eps}, 1); + float32_t* Y = svec((float[]){-1.0f}, 1); + saxpy(1, SA, X, 1, Y, 1, neg[k].m); + assert_ulong(Y[0].v, ==, neg[k].want); + free(X); free(Y); + } + return MUNIT_OK; +} + +// N==0 is a no-op: Y is left untouched. +MunitResult test_saxpy_zero(const MunitParameter params[], + void* user_data_or_fixture) { + const float32_t SA = { SB_REAL32_ONE }; + float32_t* SX = svec((float[]){42.0f}, 1); + float32_t* SY = svec((float[]){7.0f}, 1); + saxpy(0, SA, SX, 1, SY, 1, 'n'); + float32_t* RY = svec((float[]){7.0f}, 1); + assert_ulong(SY[0].v, ==, RY[0].v); + free(SX); free(SY); free(RY); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_scopy.c b/tests/blas/level1/test_scopy.c index 99fd3f1..0d8c86d 100644 --- a/tests/blas/level1/test_scopy.c +++ b/tests/blas/level1/test_scopy.c @@ -49,3 +49,15 @@ MunitResult test_scopy_stride(const MunitParameter params[], return MUNIT_OK; } + +// N==0 is a no-op: Y is left untouched. +MunitResult test_scopy_zero(const MunitParameter params[], + void* user_data_or_fixture) { + float32_t* SX = svec((float[]){42.0f}, 1); + float32_t* SY = svec((float[]){7.0f}, 1); + scopy(0, SX, 1, SY, 1, 'n'); + float32_t* RY = svec((float[]){7.0f}, 1); + assert_ulong(SY[0].v, ==, RY[0].v); + free(SX); free(SY); free(RY); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_sdot.c b/tests/blas/level1/test_sdot.c index 8e9086b..6966896 100644 --- a/tests/blas/level1/test_sdot.c +++ b/tests/blas/level1/test_sdot.c @@ -63,3 +63,14 @@ MunitResult test_sdot_neg_stride(const MunitParameter params[], return MUNIT_OK; } + +// N==0 is a no-op: returns 0. +MunitResult test_sdot_zero(const MunitParameter params[], + void* user_data_or_fixture) { + float32_t* SX = svec((float[]){42.0f}, 1); + float32_t* SY = svec((float[]){7.0f}, 1); + float32_t r = sdot(0, SX, 1, SY, 1, 'n'); + assert_ulong(r.v, ==, (uint32_t)SB_REAL32_ZERO); + free(SX); free(SY); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_snrm2.c b/tests/blas/level1/test_snrm2.c index 4857db7..7ee4bc1 100644 --- a/tests/blas/level1/test_snrm2.c +++ b/tests/blas/level1/test_snrm2.c @@ -41,3 +41,13 @@ MunitResult test_snrm2_stride(const MunitParameter params[], return MUNIT_OK; } + +// N==0 is a no-op: returns 0. +MunitResult test_snrm2_zero(const MunitParameter params[], + void* user_data_or_fixture) { + float32_t* SX = svec((float[]){42.0f}, 1); + float32_t r = snrm2(0, SX, 1, 'n'); + assert_ulong(r.v, ==, (uint32_t)SB_REAL32_ZERO); + free(SX); + return MUNIT_OK; +} diff --git a/tests/blas/level1/test_sscal.c b/tests/blas/level1/test_sscal.c index a4d6c51..7adb08c 100644 --- a/tests/blas/level1/test_sscal.c +++ b/tests/blas/level1/test_sscal.c @@ -55,3 +55,15 @@ MunitResult test_sscal_stride(const MunitParameter params[], return MUNIT_OK; } + +// N==0 is a no-op: X is left untouched. +MunitResult test_sscal_zero(const MunitParameter params[], + void* user_data_or_fixture) { + const float32_t SA = { SB_REAL32_ZERO }; + float32_t* SX = svec((float[]){42.0f}, 1); + sscal(0, SA, SX, 1, 'n'); + float32_t* RX = svec((float[]){42.0f}, 1); + assert_ulong(SX[0].v, ==, RX[0].v); + free(SX); free(RX); + return MUNIT_OK; +} diff --git a/tests/test_all.c b/tests/test_all.c index 6bd385f..57804ca 100644 --- a/tests/test_all.c +++ b/tests/test_all.c @@ -12,6 +12,14 @@ int main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) { {"/test_saxpy_stride", test_saxpy_stride, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {"/test_saxpy_neg_stride", test_saxpy_neg_stride, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {"/test_saxpy_rounding", test_saxpy_rounding, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_sasum_zero", test_sasum_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_sdot_zero", test_sdot_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_snrm2_zero", test_snrm2_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_saxpy_zero", test_saxpy_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_scopy_zero", test_scopy_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_sscal_zero", test_sscal_zero, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_saxpy_rounding_modes", test_saxpy_rounding_modes, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {"/test_qasum_layout", test_qasum_layout, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {"/test_scopy_all", test_scopy_all, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {"/test_scopy_stride", test_scopy_stride, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {"/test_sdot_0", test_sdot_0, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},