aboutsummaryrefslogtreecommitdiff
path: root/pseudocode-lib
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 15:20:26 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 15:20:26 +0000
commit9ef1cc2424e5afa21d3980b56046f5fb71bff89f (patch)
tree7b1fff9f3114601e3fa7a29ecc9e31481a4bccf6 /pseudocode-lib
parent5c491c54a22044ef1570c3fb709e14264e367fb1 (diff)
feat(output)!: print REAL values in shortest round-trip form
OUTPUT and NUM_TO_STR now share Python repr formatting instead of %f. BREAKING CHANGE: REAL output changes from six fixed decimals (2.500000) to the shortest round-trip text (2.5). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'pseudocode-lib')
-rw-r--r--pseudocode-lib/README.md2
-rw-r--r--pseudocode-lib/string.cc23
2 files changed, 23 insertions, 2 deletions
diff --git a/pseudocode-lib/README.md b/pseudocode-lib/README.md
index 1e360a4..4e70a44 100644
--- a/pseudocode-lib/README.md
+++ b/pseudocode-lib/README.md
@@ -34,7 +34,7 @@ The library provides `&`, six case-sensitive content comparisons, and these ordi
- `LCASE(CHAR)` and `UCASE(CHAR)` return CHAR, following guide section 5.5. STRING overloads return independent strings. Nonletters and embedded NUL bytes are preserved; use ASCII for predictable case conversion.
- `ASC(CHAR)` and `ASC(STRING)` return the unsigned character or first-byte code; an empty string returns zero.
- `CHR(Code)` returns a one-byte STRING containing the low eight bits. `LENGTH(CHR(0))` is 1.
-- `NUM_TO_STR(INTEGER)` and `NUM_TO_STR(REAL)` use C++17 `std::to_string`, with six fractional digits for REAL.
+- `NUM_TO_STR(INTEGER)` and `NUM_TO_STR(REAL)` convert INTEGER to decimal text and REAL to the shortest round-trip text (Python `repr` style, identical to OUTPUT: `2.0`, `0.1`, `1e-07`, `inf`).
- `STR_TO_NUM(S)` returns REAL through the same `parseNumber` helper as IS_NUM (`strtod`, full consumption). Invalid text and trailing junk throw `STR_TO_NUM: '<text>' is not a number`; overflow throws `... is out of the REAL range`; underflow is accepted.
- `IS_NUM(S)` agrees with STR_TO_NUM: full consumption by `strtod` and no overflow, without throwing. Leading whitespace is accepted; trailing whitespace, trailing junk, empty input, embedded NUL bytes, and overflowing values (`1e999`) are rejected.
diff --git a/pseudocode-lib/string.cc b/pseudocode-lib/string.cc
index 34f1ec5..a4c7343 100644
--- a/pseudocode-lib/string.cc
+++ b/pseudocode-lib/string.cc
@@ -23,6 +23,27 @@ decltype(auto) checked(Operation &&Op) {
}
std::string &text(void *Handle) { return *static_cast<std::string *>(Handle); }
+// Python-repr-style shortest round-trip text for a REAL. Keep in sync with getOutputRealHelper in
+// lib/CodeGen/OutputGen.cc, which OUTPUT uses.
+std::string realText(double Number) {
+ if (std::isnan(Number)) return "nan";
+ if (std::isinf(Number)) return Number < 0 ? "-inf" : "inf";
+ char Buffer[64];
+ double Abs = std::fabs(Number);
+ if (Abs == 0 || (Abs >= 1e-4 && Abs < 1e16)) {
+ for (int Decimals = 1; Decimals < 40; ++Decimals) {
+ std::snprintf(Buffer, sizeof Buffer, "%.*f", Decimals, Number);
+ if (std::strtod(Buffer, nullptr) == Number) break;
+ }
+ } else {
+ for (int Precision = 0; Precision < 17; ++Precision) {
+ std::snprintf(Buffer, sizeof Buffer, "%.*e", Precision, Number);
+ if (std::strtod(Buffer, nullptr) == Number) break;
+ }
+ }
+ return Buffer;
+}
+
enum class NumberStatus { Ok, Invalid, OutOfRange };
// Shared by IS_NUM and STR_TO_NUM so that they always agree.
@@ -70,7 +91,7 @@ void _string_impl_from_int(void *Target, long long Number) {
checked([&] { text(Target) = std::to_string(Number); });
}
void _string_impl_from_real(void *Target, double Number) {
- checked([&] { text(Target) = std::to_string(Number); });
+ checked([&] { text(Target) = realText(Number); });
}
double _string_impl_to_real(void *Handle) {
return checked([&] {