From d2a9122176c792f0445736639dba05b1817eb1f7 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:48:09 -0400 Subject: [PATCH 1/2] Add `fmt` tests for printing complex and quaternion types --- tests/core/fmt/test_core_fmt.odin | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 82d009ac6..84b273f90 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -1,8 +1,10 @@ package test_core_fmt +import "base:runtime" import "core:fmt" import "core:os" import "core:testing" +import "core:math" import "core:mem" TEST_count := 0 @@ -61,6 +63,70 @@ test_fmt_memory :: proc(t: ^testing.T) { check(t, "0b", "%m", u8(0)) } +@(test) +test_fmt_complex_quaternion :: proc(t: ^testing.T) { + neg_inf := math.inf_f64(-1) + pos_inf := math.inf_f64(+1) + neg_zero := f64(0h80000000_00000000) + nan := math.nan_f64() + + // NOTE(Feoramund): Doing it this way, because complex construction is broken. + // Reported in issue #3665. + c: complex128 + cptr := cast(^runtime.Raw_Complex128)&c + + cptr^ = {0, 0} + check(t, "0+0i", "%v", c) + cptr^ = {1, 1} + check(t, "1+1i", "%v", c) + cptr^ = {1, 0} + check(t, "1+0i", "%v", c) + cptr^ = {-1, -1} + check(t, "-1-1i", "%v", c) + cptr^ = {0, neg_zero} + check(t, "0-0i", "%v", c) + cptr^ = {nan, nan} + check(t, "NaNNaNi", "%v", c) + cptr^ = {pos_inf, pos_inf} + check(t, "+Inf+Infi", "%v", c) + cptr^ = {neg_inf, neg_inf} + check(t, "-Inf-Infi", "%v", c) + + // Check forced plus signs. + cptr^ = {0, neg_zero} + check(t, "+0-0i", "%+v", c) + cptr^ = {1, 1} + check(t, "+1+1i", "%+v", c) + cptr^ = {nan, nan} + check(t, "NaNNaNi", "%+v", c) + cptr^ = {pos_inf, pos_inf} + check(t, "+Inf+Infi", "%+v", c) + cptr^ = {neg_inf, neg_inf} + check(t, "-Inf-Infi", "%+v", c) + + // Remember that the real number is the last in a quaternion's data layout, + // opposed to a complex, where it is the first. + q: quaternion256 + qptr := cast(^runtime.Raw_Quaternion256)&q + + qptr^ = {0, 0, 0, 0} + check(t, "0+0i+0j+0k", "%v", q) + qptr^ = {1, 1, 1, 1} + check(t, "1+1i+1j+1k", "%v", q) + qptr^ = {2, 3, 4, 1} + check(t, "1+2i+3j+4k", "%v", q) + qptr^ = {-1, -1, -1, -1} + check(t, "-1-1i-1j-1k", "%v", q) + qptr^ = {2, neg_zero, neg_zero, 1} + check(t, "1+2i-0j-0k", "%v", q) + qptr^ = {neg_inf, neg_inf, neg_inf, -1} + check(t, "-1-Infi-Infj-Infk", "%v", q) + qptr^ = {pos_inf, pos_inf, pos_inf, -1} + check(t, "-1+Infi+Infj+Infk", "%v", q) + qptr^ = {nan, nan, nan, -1} + check(t, "-1NaNiNaNjNaNk", "%v", q) +} + @(test) test_fmt_doc_examples :: proc(t: ^testing.T) { // C-like syntax From eb93779f630870b66103c559b07b855288dc8aa4 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:49:38 -0400 Subject: [PATCH 2/2] Fix duplicate sign printing of complex and quaternion types Negative zero wasn't being detected (so it would appear as `+-0`), and `+Inf` was appearing as `++Inf` when imaginary. --- core/fmt/fmt.odin | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 62cd95968..f43683d11 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2,6 +2,7 @@ package fmt import "base:intrinsics" import "base:runtime" +import "core:math" import "core:math/bits" import "core:mem" import "core:io" @@ -2968,6 +2969,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_bit_field(fi, v, verb, info, "") } } +// This proc helps keep some of the code around whether or not to print an +// intermediate plus sign in complexes and quaternions more readable. +@(private) +_cq_should_print_intermediate_plus :: proc "contextless" (fi: ^Info, f: f64) -> bool { + if !fi.plus && f >= 0 { + #partial switch math.classify(f) { + case .Neg_Zero, .Inf: + // These two classes print their own signs. + return false + case: + return true + } + } + return false +} // Formats a complex number based on the given formatting verb // // Inputs: @@ -2981,7 +2997,7 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { case 'f', 'F', 'v', 'h', 'H', 'w': r, i := real(c), imag(c) fmt_float(fi, r, bits/2, verb) - if !fi.plus && i >= 0 { + if _cq_should_print_intermediate_plus(fi, i) { io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/2, verb) @@ -3007,19 +3023,19 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_float(fi, r, bits/4, verb) - if !fi.plus && i >= 0 { + if _cq_should_print_intermediate_plus(fi, i) { io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/4, verb) io.write_rune(fi.writer, 'i', &fi.n) - if !fi.plus && j >= 0 { + if _cq_should_print_intermediate_plus(fi, j) { io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, j, bits/4, verb) io.write_rune(fi.writer, 'j', &fi.n) - if !fi.plus && k >= 0 { + if _cq_should_print_intermediate_plus(fi, k) { io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, k, bits/4, verb)