| 1 | //===-- unittests/Runtime/NumericalFormatTest.cpp ---------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "CrashHandlerFixture.h" |
| 10 | #include "flang-rt/runtime/descriptor.h" |
| 11 | #include "flang/Runtime/io-api.h" |
| 12 | #include <algorithm> |
| 13 | #include <array> |
| 14 | #include <cstring> |
| 15 | #include <gtest/gtest.h> |
| 16 | #include <tuple> |
| 17 | |
| 18 | using namespace Fortran::runtime; |
| 19 | using namespace Fortran::runtime::io; |
| 20 | |
| 21 | static bool CompareFormattedStrings( |
| 22 | const std::string &expect, const std::string &got) { |
| 23 | std::string want{expect}; |
| 24 | want.resize(n: got.size(), c: ' '); |
| 25 | return want == got; |
| 26 | } |
| 27 | |
| 28 | static bool CompareFormattedStrings( |
| 29 | const char *expect, const std::string &&got) { |
| 30 | return CompareFormattedStrings(expect: std::string(expect), got: std::move(got)); |
| 31 | } |
| 32 | |
| 33 | // Perform format and compare the result with expected value |
| 34 | static bool CompareFormatReal( |
| 35 | const char *format, double x, const char *expect, std::string &got) { |
| 36 | char buffer[800]; |
| 37 | auto cookie{IONAME(BeginInternalFormattedOutput)( |
| 38 | buffer, sizeof buffer, format, std::strlen(format))}; |
| 39 | EXPECT_TRUE(IONAME(OutputReal64)(cookie, x)); |
| 40 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 41 | EXPECT_EQ(status, 0); |
| 42 | got = std::string{buffer, sizeof buffer}; |
| 43 | auto lastNonBlank{got.find_last_not_of(s: " " )}; |
| 44 | if (lastNonBlank != std::string::npos) { |
| 45 | got.resize(n: lastNonBlank + 1); |
| 46 | } |
| 47 | return CompareFormattedStrings(expect, got); |
| 48 | } |
| 49 | |
| 50 | // Convert raw uint64 into double, perform format, and compare with expected |
| 51 | static bool CompareFormatReal(const char *format, std::uint64_t xInt, |
| 52 | const char *expect, std::string &got) { |
| 53 | double x; |
| 54 | static_assert(sizeof(double) == sizeof(std::uint64_t), |
| 55 | "Size of double != size of uint64_t!" ); |
| 56 | std::memcpy(dest: &x, src: &xInt, n: sizeof xInt); |
| 57 | return CompareFormatReal(format, x, expect, got); |
| 58 | } |
| 59 | |
| 60 | static bool CompareFormatInteger( |
| 61 | const char *format, std::int64_t x, const char *expect, std::string &got) { |
| 62 | char buffer[800]; |
| 63 | auto cookie{IONAME(BeginInternalFormattedOutput)( |
| 64 | buffer, sizeof buffer, format, std::strlen(format))}; |
| 65 | EXPECT_TRUE(IONAME(OutputInteger64)(cookie, x)); |
| 66 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 67 | EXPECT_EQ(status, 0); |
| 68 | got = std::string{buffer, sizeof buffer}; |
| 69 | auto lastNonBlank{got.find_last_not_of(s: " " )}; |
| 70 | if (lastNonBlank != std::string::npos) { |
| 71 | got.resize(n: lastNonBlank + 1); |
| 72 | } |
| 73 | return CompareFormattedStrings(expect, got); |
| 74 | } |
| 75 | |
| 76 | struct IOApiTests : CrashHandlerFixture {}; |
| 77 | |
| 78 | TEST(IOApiTests, HelloWorldOutputTest) { |
| 79 | static constexpr int bufferSize{32}; |
| 80 | char buffer[bufferSize]; |
| 81 | |
| 82 | // Create format for all types and values to be written |
| 83 | const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)" }; |
| 84 | auto cookie{IONAME(BeginInternalFormattedOutput)( |
| 85 | buffer, bufferSize, format, std::strlen(format))}; |
| 86 | |
| 87 | // Write string, integer, and logical values to buffer |
| 88 | IONAME(OutputAscii)(cookie, "WORLD" , 5); |
| 89 | IONAME(OutputInteger64)(cookie, 678); |
| 90 | IONAME(OutputInteger32)(cookie, 0xfeedface); |
| 91 | IONAME(OutputLogical)(cookie, true); |
| 92 | |
| 93 | // Ensure IO succeeded |
| 94 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 95 | ASSERT_EQ(status, 0) << "hello: '" << format << "' failed, status " |
| 96 | << static_cast<int>(status); |
| 97 | |
| 98 | // Ensure final buffer matches expected string output |
| 99 | static const std::string expect{"HELLO, WORLD 678 0xFEEDFACE T" }; |
| 100 | ASSERT_TRUE( |
| 101 | CompareFormattedStrings(expect, std::string{buffer, sizeof buffer})) |
| 102 | << "Expected '" << expect << "', got " << buffer; |
| 103 | } |
| 104 | |
| 105 | TEST(IOApiTests, MultilineOutputTest) { |
| 106 | // Allocate buffer for multiline output |
| 107 | static constexpr int numLines{5}; |
| 108 | static constexpr int lineLength{32}; |
| 109 | char buffer[numLines][lineLength]; |
| 110 | |
| 111 | // Create descriptor for entire buffer |
| 112 | static constexpr int staticDescriptorMaxRank{1}; |
| 113 | StaticDescriptor<staticDescriptorMaxRank> wholeStaticDescriptor; |
| 114 | Descriptor &whole{wholeStaticDescriptor.descriptor()}; |
| 115 | static const SubscriptValue extent[]{numLines}; |
| 116 | whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer, |
| 117 | staticDescriptorMaxRank, extent, CFI_attribute_pointer); |
| 118 | whole.Dump(stderr); |
| 119 | whole.Check(); |
| 120 | |
| 121 | // Create descriptor for buffer section |
| 122 | StaticDescriptor<staticDescriptorMaxRank> sectionStaticDescriptor; |
| 123 | Descriptor §ion{sectionStaticDescriptor.descriptor()}; |
| 124 | static const SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1}; |
| 125 | section.Establish(whole.type(), /*elementBytes=*/whole.ElementBytes(), |
| 126 | nullptr, /*maxRank=*/staticDescriptorMaxRank, extent, |
| 127 | CFI_attribute_pointer); |
| 128 | |
| 129 | // Ensure C descriptor address `section.raw()` is updated without error |
| 130 | const auto error{ |
| 131 | CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}; |
| 132 | ASSERT_EQ(error, 0) << "multiline: CFI_section failed: " << error; |
| 133 | section.Dump(stderr); |
| 134 | section.Check(); |
| 135 | |
| 136 | // Create format string and initialize IO operation |
| 137 | const char *format{ |
| 138 | "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))" }; |
| 139 | auto cookie{IONAME(BeginInternalArrayFormattedOutput)( |
| 140 | section, format, std::strlen(format))}; |
| 141 | |
| 142 | // Fill last line with periods |
| 143 | std::memset(s: buffer[numLines - 1], c: '.', n: lineLength); |
| 144 | |
| 145 | // Write data to buffer |
| 146 | IONAME(OutputAscii)(cookie, "WORLD" , 5); |
| 147 | IONAME(OutputAscii)(cookie, "HELLO" , 5); |
| 148 | IONAME(OutputInteger64)(cookie, 789); |
| 149 | for (int j{666}; j <= 999; j += 111) { |
| 150 | IONAME(OutputInteger64)(cookie, j); |
| 151 | } |
| 152 | |
| 153 | // Ensure no errors occured in write operations above |
| 154 | const auto status{IONAME(EndIoStatement)(cookie)}; |
| 155 | ASSERT_EQ(status, 0) << "multiline: '" << format << "' failed, status " |
| 156 | << static_cast<int>(status); |
| 157 | |
| 158 | static const std::string expect{">HELLO, WORLD <" |
| 159 | " " |
| 160 | "789 abcd 666 777" |
| 161 | " 888 999 " |
| 162 | "................................" }; |
| 163 | // Ensure formatted string matches expected output |
| 164 | EXPECT_TRUE( |
| 165 | CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer})) |
| 166 | << "Expected '" << expect << "' but got '" |
| 167 | << std::string{buffer[0], sizeof buffer} << "'" ; |
| 168 | } |
| 169 | |
| 170 | TEST(IOApiTests, ListInputTest) { |
| 171 | static const char input[]{",1*,(5.,6.),(7.0,8.0)" }; |
| 172 | auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; |
| 173 | |
| 174 | // Create real values for IO tests |
| 175 | static constexpr int numRealValues{8}; |
| 176 | float z[numRealValues]; |
| 177 | for (int j{0}; j < numRealValues; ++j) { |
| 178 | z[j] = -(j + 1); |
| 179 | } |
| 180 | |
| 181 | // Ensure reading complex values to floats does not result in an error |
| 182 | for (int j{0}; j < numRealValues; j += 2) { |
| 183 | ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j])) |
| 184 | << "InputComplex32 failed with value " << z[j]; |
| 185 | } |
| 186 | |
| 187 | // Ensure no IO errors occured during IO operations above |
| 188 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 189 | ASSERT_EQ(status, 0) << "Failed complex list-directed input, status " |
| 190 | << static_cast<int>(status); |
| 191 | |
| 192 | // Ensure writing complex values from floats does not result in an error |
| 193 | static constexpr int bufferSize{39}; |
| 194 | char output[bufferSize]; |
| 195 | output[bufferSize - 1] = '\0'; |
| 196 | cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1); |
| 197 | for (int j{0}; j < numRealValues; j += 2) { |
| 198 | ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1])) |
| 199 | << "OutputComplex32 failed when outputting value " << z[j] << ", " |
| 200 | << z[j + 1]; |
| 201 | } |
| 202 | |
| 203 | // Ensure no IO errors occured during IO operations above |
| 204 | status = IONAME(EndIoStatement)(cookie); |
| 205 | ASSERT_EQ(status, 0) << "Failed complex list-directed output, status " |
| 206 | << static_cast<int>(status); |
| 207 | |
| 208 | // Verify output buffer against expected value |
| 209 | static const char expect[bufferSize]{ |
| 210 | " (-1.,-2.) (-3.,-4.) (5.,6.) (7.,8.) " }; |
| 211 | ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0) |
| 212 | << "Failed complex list-directed output, expected '" << expect |
| 213 | << "', but got '" << output << "'" ; |
| 214 | } |
| 215 | |
| 216 | TEST(IOApiTests, DescriptorOutputTest) { |
| 217 | static constexpr int bufferSize{10}; |
| 218 | char buffer[bufferSize]; |
| 219 | const char *format{"(2A4)" }; |
| 220 | auto cookie{IONAME(BeginInternalFormattedOutput)( |
| 221 | buffer, bufferSize, format, std::strlen(format))}; |
| 222 | |
| 223 | // Create descriptor for output |
| 224 | static constexpr int staticDescriptorMaxRank{1}; |
| 225 | StaticDescriptor<staticDescriptorMaxRank> staticDescriptor; |
| 226 | Descriptor &desc{staticDescriptor.descriptor()}; |
| 227 | static constexpr int subscriptExtent{2}; |
| 228 | static const SubscriptValue extent[]{subscriptExtent}; |
| 229 | |
| 230 | // Manually write to descriptor buffer |
| 231 | static constexpr int dataLength{4}; |
| 232 | char data[subscriptExtent][dataLength]; |
| 233 | std::memcpy(dest: data[0], src: "ABCD" , n: dataLength); |
| 234 | std::memcpy(dest: data[1], src: "EFGH" , n: dataLength); |
| 235 | desc.Establish(TypeCode{CFI_type_char}, dataLength, &data, |
| 236 | staticDescriptorMaxRank, extent); |
| 237 | desc.Dump(stderr); |
| 238 | desc.Check(); |
| 239 | IONAME(OutputDescriptor)(cookie, desc); |
| 240 | |
| 241 | // Ensure no errors were encountered in initializing the cookie and descriptor |
| 242 | auto formatStatus{IONAME(EndIoStatement)(cookie)}; |
| 243 | ASSERT_EQ(formatStatus, 0) |
| 244 | << "descrOutputTest: '" << format << "' failed, status " |
| 245 | << static_cast<int>(formatStatus); |
| 246 | |
| 247 | // Ensure buffer matches expected output |
| 248 | EXPECT_TRUE( |
| 249 | CompareFormattedStrings("ABCDEFGH " , std::string{buffer, sizeof buffer})) |
| 250 | << "descrOutputTest: formatted: got '" |
| 251 | << std::string{buffer, sizeof buffer} << "'" ; |
| 252 | |
| 253 | // Begin list-directed output on cookie by descriptor |
| 254 | cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer); |
| 255 | IONAME(OutputDescriptor)(cookie, desc); |
| 256 | |
| 257 | // Ensure list-directed output does not result in an IO error |
| 258 | auto listDirectedStatus{IONAME(EndIoStatement)(cookie)}; |
| 259 | ASSERT_EQ(listDirectedStatus, 0) |
| 260 | << "descrOutputTest: list-directed failed, status " |
| 261 | << static_cast<int>(listDirectedStatus); |
| 262 | |
| 263 | // Ensure buffer matches expected output |
| 264 | EXPECT_TRUE( |
| 265 | CompareFormattedStrings(" ABCDEFGH " , std::string{buffer, sizeof buffer})) |
| 266 | << "descrOutputTest: list-directed: got '" |
| 267 | << std::string{buffer, sizeof buffer} << "'" ; |
| 268 | } |
| 269 | |
| 270 | //------------------------------------------------------------------------------ |
| 271 | /// Tests for output formatting real values |
| 272 | //------------------------------------------------------------------------------ |
| 273 | |
| 274 | TEST(IOApiTests, FormatZeroes) { |
| 275 | static constexpr std::pair<const char *, const char *> zeroes[]{ |
| 276 | {"(E32.17,';')" , " 0.00000000000000000E+00;" }, |
| 277 | {"(F32.17,';')" , " 0.00000000000000000;" }, |
| 278 | {"(G32.17,';')" , " 0.0000000000000000 ;" }, |
| 279 | {"(DC,E32.17,';')" , " 0,00000000000000000E+00;" }, |
| 280 | {"(DC,F32.17,';')" , " 0,00000000000000000;" }, |
| 281 | {"(DC,G32.17,';')" , " 0,0000000000000000 ;" }, |
| 282 | {"(D32.17,';')" , " 0.00000000000000000D+00;" }, |
| 283 | {"(E32.17E1,';')" , " 0.00000000000000000E+0;" }, |
| 284 | {"(G32.17E1,';')" , " 0.0000000000000000 ;" }, |
| 285 | {"(E32.17E0,';')" , " 0.00000000000000000E+0;" }, |
| 286 | {"(G32.17E0,';')" , " 0.0000000000000000 ;" }, |
| 287 | {"(1P,E32.17,';')" , " 0.00000000000000000E+00;" }, |
| 288 | {"(1PE32.17,';')" , " 0.00000000000000000E+00;" }, // no comma |
| 289 | {"(1P,F32.17,';')" , " 0.00000000000000000;" }, |
| 290 | {"(1P,G32.17,';')" , " 0.0000000000000000 ;" }, |
| 291 | {"(2P,E32.17,';')" , " 00.0000000000000000E+00;" }, |
| 292 | {"(-1P,E32.17,';')" , " 0.00000000000000000E+00;" }, |
| 293 | {"(EX32.17,';')" , " 0X0.00000000000000000P+0;" }, |
| 294 | {"(DC,EX32.17,';')" , " 0X0,00000000000000000P+0;" }, |
| 295 | {"(G0,';')" , "0.;" }, |
| 296 | }; |
| 297 | |
| 298 | for (auto const &[format, expect] : zeroes) { |
| 299 | std::string got; |
| 300 | ASSERT_TRUE(CompareFormatReal(format, 0.0, expect, got)) |
| 301 | << "Failed to format " << format << ", expected '" << expect |
| 302 | << "', got '" << got << "'" ; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | TEST(IOApiTests, FormatOnes) { |
| 307 | static constexpr std::pair<const char *, const char *> ones[]{ |
| 308 | {"(E32.17,';')" , " 0.10000000000000000E+01;" }, |
| 309 | {"(F32.17,';')" , " 1.00000000000000000;" }, |
| 310 | {"(G32.17,';')" , " 1.0000000000000000 ;" }, |
| 311 | {"(E32.17E1,';')" , " 0.10000000000000000E+1;" }, |
| 312 | {"(G32.17E1,';')" , " 1.0000000000000000 ;" }, |
| 313 | {"(E32.17E0,';')" , " 0.10000000000000000E+1;" }, |
| 314 | {"(G32.17E0,';')" , " 1.0000000000000000 ;" }, |
| 315 | {"(E32.17E4,';')" , " 0.10000000000000000E+0001;" }, |
| 316 | {"(G32.17E4,';')" , " 1.0000000000000000 ;" }, |
| 317 | {"(1P,E32.17,';')" , " 1.00000000000000000E+00;" }, |
| 318 | {"(1PE32.17,';')" , " 1.00000000000000000E+00;" }, // no comma |
| 319 | {"(1P,F32.17,';')" , " 10.00000000000000000;" }, |
| 320 | {"(1P,G32.17,';')" , " 1.0000000000000000 ;" }, |
| 321 | {"(ES32.17,';')" , " 1.00000000000000000E+00;" }, |
| 322 | {"(2P,E32.17,';')" , " 10.0000000000000000E-01;" }, |
| 323 | {"(2P,G32.17,';')" , " 1.0000000000000000 ;" }, |
| 324 | {"(-1P,E32.17,';')" , " 0.01000000000000000E+02;" }, |
| 325 | {"(-1P,G32.17,';')" , " 1.0000000000000000 ;" }, |
| 326 | {"(EX32.17,';')" , " 0X8.00000000000000000P-3;" }, |
| 327 | {"(DC,EX32.17,';')" , " 0X8,00000000000000000P-3;" }, |
| 328 | {"(G0,';')" , "1.;" }, |
| 329 | }; |
| 330 | |
| 331 | for (auto const &[format, expect] : ones) { |
| 332 | std::string got; |
| 333 | ASSERT_TRUE(CompareFormatReal(format, 1.0, expect, got)) |
| 334 | << "Failed to format " << format << ", expected '" << expect |
| 335 | << "', got '" << got << "'" ; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | TEST(IOApiTests, FormatNegativeOnes) { |
| 340 | static constexpr std::tuple<const char *, const char *> negOnes[]{ |
| 341 | {"(E32.17,';')" , " -0.10000000000000000E+01;" }, |
| 342 | {"(F32.17,';')" , " -1.00000000000000000;" }, |
| 343 | {"(G32.17,';')" , " -1.0000000000000000 ;" }, |
| 344 | {"(EX32.17,';')" , " -0X8.00000000000000000P-3;" }, |
| 345 | {"(G0,';')" , "-1.;" }, |
| 346 | }; |
| 347 | for (auto const &[format, expect] : negOnes) { |
| 348 | std::string got; |
| 349 | ASSERT_TRUE(CompareFormatReal(format, -1.0, expect, got)) |
| 350 | << "Failed to format " << format << ", expected '" << expect |
| 351 | << "', got '" << got << "'" ; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | // Each test case contains a raw uint64, a format string for a real value, and |
| 356 | // the expected resulting string from formatting the raw uint64. The double |
| 357 | // representation of the uint64 is commented above each test case. |
| 358 | TEST(IOApiTests, FormatDoubleValues) { |
| 359 | |
| 360 | using TestCaseTy = std::tuple<std::uint64_t, |
| 361 | std::vector<std::tuple<const char *, const char *>>>; |
| 362 | static const std::vector<TestCaseTy> testCases{ |
| 363 | {// -0 |
| 364 | 0x8000000000000000, |
| 365 | { |
| 366 | {"(E9.1,';')" , " -0.0E+00;" }, |
| 367 | {"(F4.0,';')" , " -0.;" }, |
| 368 | {"(F0.1,';')" , "-.0;" }, |
| 369 | {"(G8.0,';')" , "-0.0E+00;" }, |
| 370 | {"(G8.1,';')" , " -0. ;" }, |
| 371 | {"(G0,';')" , "-0.;" }, |
| 372 | {"(E9.1,';')" , " -0.0E+00;" }, |
| 373 | {"(EX9.1,';')" , "-0X0.0P+0;" }, |
| 374 | }}, |
| 375 | {// +Inf |
| 376 | 0x7ff0000000000000, |
| 377 | { |
| 378 | {"(E9.1,';')" , " Inf;" }, |
| 379 | {"(F9.1,';')" , " Inf;" }, |
| 380 | {"(G9.1,';')" , " Inf;" }, |
| 381 | {"(EX9.1,';')" , " Inf;" }, |
| 382 | {"(SP,E9.1,';')" , " +Inf;" }, |
| 383 | {"(SP,F9.1,';')" , " +Inf;" }, |
| 384 | {"(SP,G9.1,';')" , " +Inf;" }, |
| 385 | {"(SP,EX9.1,';')" , " +Inf;" }, |
| 386 | {"(G0,';')" , "Inf;" }, |
| 387 | }}, |
| 388 | {// -Inf |
| 389 | 0xfff0000000000000, |
| 390 | { |
| 391 | {"(E9.1,';')" , " -Inf;" }, |
| 392 | {"(F9.1,';')" , " -Inf;" }, |
| 393 | {"(G9.1,';')" , " -Inf;" }, |
| 394 | {"(EX9.1,';')" , " -Inf;" }, |
| 395 | {"(G0,';')" , "-Inf;" }, |
| 396 | }}, |
| 397 | {// NaN |
| 398 | 0x7ff0000000000001, |
| 399 | { |
| 400 | {"(E9.1,';')" , " NaN;" }, |
| 401 | {"(F9.1,';')" , " NaN;" }, |
| 402 | {"(G9.1,';')" , " NaN;" }, |
| 403 | {"(EX9.1,';')" , " NaN;" }, |
| 404 | {"(G0,';')" , "NaN;" }, |
| 405 | }}, |
| 406 | {// NaN (sign irrelevant) |
| 407 | 0xfff0000000000001, |
| 408 | { |
| 409 | {"(E9.1,';')" , " NaN;" }, |
| 410 | {"(F9.1,';')" , " NaN;" }, |
| 411 | {"(G9.1,';')" , " NaN;" }, |
| 412 | {"(SP,E9.1,';')" , " NaN;" }, |
| 413 | {"(SP,F9.1,';')" , " NaN;" }, |
| 414 | {"(SP,G9.1,';')" , " NaN;" }, |
| 415 | {"(SP,EX9.1,';')" , " NaN;" }, |
| 416 | {"(G0,';')" , "NaN;" }, |
| 417 | }}, |
| 418 | {// 0.1 rounded |
| 419 | 0x3fb999999999999a, |
| 420 | { |
| 421 | {"(E62.55,';')" , |
| 422 | " 0.1000000000000000055511151231257827021181583404541015625E+" |
| 423 | "00;" }, |
| 424 | {"(E0.1,';')" , ".1E+00;" }, |
| 425 | {"(E0.55,';')" , |
| 426 | ".1000000000000000055511151231257827021181583404541015625E+" |
| 427 | "00;" }, |
| 428 | {"(E0,';')" , ".1E+00;" }, |
| 429 | {"(F58.55,';')" , |
| 430 | " 0." |
| 431 | "1000000000000000055511151231257827021181583404541015625;" }, |
| 432 | {"(F0.0,';')" , "0.;" }, |
| 433 | {"(F0.55,';')" , |
| 434 | ".1000000000000000055511151231257827021181583404541015625;" }, |
| 435 | {"(F0,';')" , ".1;" }, |
| 436 | {"(G62.55,';')" , |
| 437 | " 0.1000000000000000055511151231257827021181583404541015625 " |
| 438 | " ;" }, |
| 439 | {"(G0.0,';')" , "0.;" }, |
| 440 | {"(G0.55,';')" , |
| 441 | ".1000000000000000055511151231257827021181583404541015625;" }, |
| 442 | {"(G0,';')" , ".1;" }, |
| 443 | {"(EX20.12,';')" , " 0XC.CCCCCCCCCCCDP-7;" }, |
| 444 | }}, |
| 445 | {// 1.5 |
| 446 | 0x3ff8000000000000, |
| 447 | { |
| 448 | {"(E9.2,';')" , " 0.15E+01;" }, |
| 449 | {"(F4.1,';')" , " 1.5;" }, |
| 450 | {"(G7.1,';')" , " 2. ;" }, |
| 451 | {"(EX9.1,';')" , " 0XC.0P-3;" }, |
| 452 | {"(RN,E8.1,';')" , " 0.2E+01;" }, |
| 453 | {"(RN,F3.0,';')" , " 2.;" }, |
| 454 | {"(RN,G7.0,';')" , " 0.E+01;" }, |
| 455 | {"(RN,G7.1,';')" , " 2. ;" }, |
| 456 | {"(RD,E8.1,';')" , " 0.1E+01;" }, |
| 457 | {"(RD,F3.0,';')" , " 1.;" }, |
| 458 | {"(RD,G7.0,';')" , " 0.E+01;" }, |
| 459 | {"(RD,G7.1,';')" , " 1. ;" }, |
| 460 | {"(RU,E8.1,';')" , " 0.2E+01;" }, |
| 461 | {"(RU,G7.0,';')" , " 0.E+01;" }, |
| 462 | {"(RU,G7.1,';')" , " 2. ;" }, |
| 463 | {"(RZ,E8.1,';')" , " 0.1E+01;" }, |
| 464 | {"(RZ,F3.0,';')" , " 1.;" }, |
| 465 | {"(RZ,G7.0,';')" , " 0.E+01;" }, |
| 466 | {"(RZ,G7.1,';')" , " 1. ;" }, |
| 467 | {"(RC,E8.1,';')" , " 0.2E+01;" }, |
| 468 | {"(RC,F3.0,';')" , " 2.;" }, |
| 469 | {"(RC,G7.0,';')" , " 0.E+01;" }, |
| 470 | {"(RC,G7.1,';')" , " 2. ;" }, |
| 471 | }}, |
| 472 | {// -1.5 |
| 473 | 0xbff8000000000000, |
| 474 | { |
| 475 | {"(E9.2,';')" , "-0.15E+01;" }, |
| 476 | {"(RN,E8.1,';')" , "-0.2E+01;" }, |
| 477 | {"(RD,E8.1,';')" , "-0.2E+01;" }, |
| 478 | {"(RU,E8.1,';')" , "-0.1E+01;" }, |
| 479 | {"(RZ,E8.1,';')" , "-0.1E+01;" }, |
| 480 | {"(RC,E8.1,';')" , "-0.2E+01;" }, |
| 481 | {"(EX9.1,';')" , "-0XC.0P-3;" }, |
| 482 | }}, |
| 483 | {// 2.5 |
| 484 | 0x4004000000000000, |
| 485 | { |
| 486 | {"(E9.2,';')" , " 0.25E+01;" }, |
| 487 | {"(RN,E8.1,';')" , " 0.2E+01;" }, |
| 488 | {"(RD,E8.1,';')" , " 0.2E+01;" }, |
| 489 | {"(RU,E8.1,';')" , " 0.3E+01;" }, |
| 490 | {"(RZ,E8.1,';')" , " 0.2E+01;" }, |
| 491 | {"(RC,E8.1,';')" , " 0.3E+01;" }, |
| 492 | {"(EX9.1,';')" , " 0XA.0P-2;" }, |
| 493 | }}, |
| 494 | {// -2.5 |
| 495 | 0xc004000000000000, |
| 496 | { |
| 497 | {"(E9.2,';')" , "-0.25E+01;" }, |
| 498 | {"(RN,E8.1,';')" , "-0.2E+01;" }, |
| 499 | {"(RD,E8.1,';')" , "-0.3E+01;" }, |
| 500 | {"(RU,E8.1,';')" , "-0.2E+01;" }, |
| 501 | {"(RZ,E8.1,';')" , "-0.2E+01;" }, |
| 502 | {"(RC,E8.1,';')" , "-0.3E+01;" }, |
| 503 | {"(EX9.1,';')" , "-0XA.0P-2;" }, |
| 504 | }}, |
| 505 | {// least positive nonzero subnormal |
| 506 | 1, |
| 507 | { |
| 508 | {"(E32.17,';')" , " 0.49406564584124654-323;" }, |
| 509 | {"(ES32.17,';')" , " 4.94065645841246544-324;" }, |
| 510 | {"(EN32.17,';')" , " 4.94065645841246544-324;" }, |
| 511 | {"(E759.752,';')" , |
| 512 | " 0." |
| 513 | "494065645841246544176568792868221372365059802614324764425585" |
| 514 | "682500675507270208751865299836361635992379796564695445717730" |
| 515 | "926656710355939796398774796010781878126300713190311404527845" |
| 516 | "817167848982103688718636056998730723050006387409153564984387" |
| 517 | "312473397273169615140031715385398074126238565591171026658556" |
| 518 | "686768187039560310624931945271591492455329305456544401127480" |
| 519 | "129709999541931989409080416563324524757147869014726780159355" |
| 520 | "238611550134803526493472019379026810710749170333222684475333" |
| 521 | "572083243193609238289345836806010601150616980975307834227731" |
| 522 | "832924790498252473077637592724787465608477820373446969953364" |
| 523 | "701797267771758512566055119913150489110145103786273816725095" |
| 524 | "583738973359899366480994116420570263709027924276754456522908" |
| 525 | "75386825064197182655334472656250-323;" }, |
| 526 | {"(G0,';')" , ".5E-323;" }, |
| 527 | {"(E757.750,';')" , |
| 528 | " 0." |
| 529 | "494065645841246544176568792868221372365059802614324764425585" |
| 530 | "682500675507270208751865299836361635992379796564695445717730" |
| 531 | "926656710355939796398774796010781878126300713190311404527845" |
| 532 | "817167848982103688718636056998730723050006387409153564984387" |
| 533 | "312473397273169615140031715385398074126238565591171026658556" |
| 534 | "686768187039560310624931945271591492455329305456544401127480" |
| 535 | "129709999541931989409080416563324524757147869014726780159355" |
| 536 | "238611550134803526493472019379026810710749170333222684475333" |
| 537 | "572083243193609238289345836806010601150616980975307834227731" |
| 538 | "832924790498252473077637592724787465608477820373446969953364" |
| 539 | "701797267771758512566055119913150489110145103786273816725095" |
| 540 | "583738973359899366480994116420570263709027924276754456522908" |
| 541 | "753868250641971826553344726562-323;" }, |
| 542 | {"(RN,E757.750,';')" , |
| 543 | " 0." |
| 544 | "494065645841246544176568792868221372365059802614324764425585" |
| 545 | "682500675507270208751865299836361635992379796564695445717730" |
| 546 | "926656710355939796398774796010781878126300713190311404527845" |
| 547 | "817167848982103688718636056998730723050006387409153564984387" |
| 548 | "312473397273169615140031715385398074126238565591171026658556" |
| 549 | "686768187039560310624931945271591492455329305456544401127480" |
| 550 | "129709999541931989409080416563324524757147869014726780159355" |
| 551 | "238611550134803526493472019379026810710749170333222684475333" |
| 552 | "572083243193609238289345836806010601150616980975307834227731" |
| 553 | "832924790498252473077637592724787465608477820373446969953364" |
| 554 | "701797267771758512566055119913150489110145103786273816725095" |
| 555 | "583738973359899366480994116420570263709027924276754456522908" |
| 556 | "753868250641971826553344726562-323;" }, |
| 557 | {"(RD,E757.750,';')" , |
| 558 | " 0." |
| 559 | "494065645841246544176568792868221372365059802614324764425585" |
| 560 | "682500675507270208751865299836361635992379796564695445717730" |
| 561 | "926656710355939796398774796010781878126300713190311404527845" |
| 562 | "817167848982103688718636056998730723050006387409153564984387" |
| 563 | "312473397273169615140031715385398074126238565591171026658556" |
| 564 | "686768187039560310624931945271591492455329305456544401127480" |
| 565 | "129709999541931989409080416563324524757147869014726780159355" |
| 566 | "238611550134803526493472019379026810710749170333222684475333" |
| 567 | "572083243193609238289345836806010601150616980975307834227731" |
| 568 | "832924790498252473077637592724787465608477820373446969953364" |
| 569 | "701797267771758512566055119913150489110145103786273816725095" |
| 570 | "583738973359899366480994116420570263709027924276754456522908" |
| 571 | "753868250641971826553344726562-323;" }, |
| 572 | {"(RU,E757.750,';')" , |
| 573 | " 0." |
| 574 | "494065645841246544176568792868221372365059802614324764425585" |
| 575 | "682500675507270208751865299836361635992379796564695445717730" |
| 576 | "926656710355939796398774796010781878126300713190311404527845" |
| 577 | "817167848982103688718636056998730723050006387409153564984387" |
| 578 | "312473397273169615140031715385398074126238565591171026658556" |
| 579 | "686768187039560310624931945271591492455329305456544401127480" |
| 580 | "129709999541931989409080416563324524757147869014726780159355" |
| 581 | "238611550134803526493472019379026810710749170333222684475333" |
| 582 | "572083243193609238289345836806010601150616980975307834227731" |
| 583 | "832924790498252473077637592724787465608477820373446969953364" |
| 584 | "701797267771758512566055119913150489110145103786273816725095" |
| 585 | "583738973359899366480994116420570263709027924276754456522908" |
| 586 | "753868250641971826553344726563-323;" }, |
| 587 | {"(RC,E757.750,';')" , |
| 588 | " 0." |
| 589 | "494065645841246544176568792868221372365059802614324764425585" |
| 590 | "682500675507270208751865299836361635992379796564695445717730" |
| 591 | "926656710355939796398774796010781878126300713190311404527845" |
| 592 | "817167848982103688718636056998730723050006387409153564984387" |
| 593 | "312473397273169615140031715385398074126238565591171026658556" |
| 594 | "686768187039560310624931945271591492455329305456544401127480" |
| 595 | "129709999541931989409080416563324524757147869014726780159355" |
| 596 | "238611550134803526493472019379026810710749170333222684475333" |
| 597 | "572083243193609238289345836806010601150616980975307834227731" |
| 598 | "832924790498252473077637592724787465608477820373446969953364" |
| 599 | "701797267771758512566055119913150489110145103786273816725095" |
| 600 | "583738973359899366480994116420570263709027924276754456522908" |
| 601 | "753868250641971826553344726563-323;" }, |
| 602 | {"(EX24.13,';')" , " 0X8.0000000000000P-1077;" }, |
| 603 | }}, |
| 604 | {// least positive nonzero normal |
| 605 | 0x10000000000000, |
| 606 | { |
| 607 | {"(E723.716,';')" , |
| 608 | " 0." |
| 609 | "222507385850720138309023271733240406421921598046233183055332" |
| 610 | "741688720443481391819585428315901251102056406733973103581100" |
| 611 | "515243416155346010885601238537771882113077799353200233047961" |
| 612 | "014744258363607192156504694250373420837525080665061665815894" |
| 613 | "872049117996859163964850063590877011830487479978088775374994" |
| 614 | "945158045160505091539985658247081864511353793580499211598108" |
| 615 | "576605199243335211435239014879569960959128889160299264151106" |
| 616 | "346631339366347758651302937176204732563178148566435087212282" |
| 617 | "863764204484681140761391147706280168985324411002416144742161" |
| 618 | "856716615054015428508471675290190316132277889672970737312333" |
| 619 | "408698898317506783884692609277397797285865965494109136909540" |
| 620 | "61364675687023986783152906809846172109246253967285156250-" |
| 621 | "307;" }, |
| 622 | {"(G0,';')" , ".22250738585072014E-307;" }, |
| 623 | {"(EX24.13,';')" , " 0X8.0000000000000P-1025;" }, |
| 624 | }}, |
| 625 | {// greatest finite |
| 626 | 0x7fefffffffffffffuLL, |
| 627 | { |
| 628 | {"(E32.17,';')" , " 0.17976931348623157+309;" }, |
| 629 | {"(E317.310,';')" , |
| 630 | " 0." |
| 631 | "179769313486231570814527423731704356798070567525844996598917" |
| 632 | "476803157260780028538760589558632766878171540458953514382464" |
| 633 | "234321326889464182768467546703537516986049910576551282076245" |
| 634 | "490090389328944075868508455133942304583236903222948165808559" |
| 635 | "332123348274797826204144723168738177180919299881250404026184" |
| 636 | "1248583680+309;" }, |
| 637 | {"(ES317.310,';')" , |
| 638 | " 1." |
| 639 | "797693134862315708145274237317043567980705675258449965989174" |
| 640 | "768031572607800285387605895586327668781715404589535143824642" |
| 641 | "343213268894641827684675467035375169860499105765512820762454" |
| 642 | "900903893289440758685084551339423045832369032229481658085593" |
| 643 | "321233482747978262041447231687381771809192998812504040261841" |
| 644 | "2485836800+308;" }, |
| 645 | {"(EN319.310,';')" , |
| 646 | " 179." |
| 647 | "769313486231570814527423731704356798070567525844996598917476" |
| 648 | "803157260780028538760589558632766878171540458953514382464234" |
| 649 | "321326889464182768467546703537516986049910576551282076245490" |
| 650 | "090389328944075868508455133942304583236903222948165808559332" |
| 651 | "123348274797826204144723168738177180919299881250404026184124" |
| 652 | "8583680000+306;" }, |
| 653 | {"(G0,';')" , ".17976931348623157E+309;" }, |
| 654 | {"(EX24.13,';')" , " 0XF.FFFFFFFFFFFF8P+1020;" }, |
| 655 | }}, |
| 656 | {// EX rounding |
| 657 | 0x3ff0100000000000uLL, |
| 658 | { |
| 659 | {"(F11.8,';')" , " 1.00390625;" }, |
| 660 | {"(EX10.2,';')" , " 0X8.08P-3;" }, |
| 661 | {"(EX10.1,';')" , " 0X8.0P-3;" }, |
| 662 | {"(EX10.0,';')" , " 0X8.08P-3;" }, |
| 663 | {"(EX0.0,';')" , "0X8.08P-3;" }, |
| 664 | {"(EX0,';')" , "0X8.08P-3;" }, |
| 665 | {"(RN,EX10.1,';')" , " 0X8.0P-3;" }, |
| 666 | {"(RU,EX10.1,';')" , " 0X8.1P-3;" }, |
| 667 | {"(RD,EX10.1,';')" , " 0X8.0P-3;" }, |
| 668 | {"(RZ,EX10.1,';')" , " 0X8.0P-3;" }, |
| 669 | {"(RC,EX10.1,';')" , " 0X8.1P-3;" }, |
| 670 | {"(RN,EX10.0,';')" , " 0X8.08P-3;" }, |
| 671 | {"(RU,EX10.0,';')" , " 0X8.08P-3;" }, |
| 672 | {"(RD,EX10.0,';')" , " 0X8.08P-3;" }, |
| 673 | {"(RZ,EX10.0,';')" , " 0X8.08P-3;" }, |
| 674 | {"(RC,EX10.0,';')" , " 0X8.08P-3;" }, |
| 675 | }}, |
| 676 | {// EX rounding |
| 677 | 0xbff0100000000000uLL, |
| 678 | { |
| 679 | {"(F11.8,';')" , "-1.00390625;" }, |
| 680 | {"(EX10.2,';')" , "-0X8.08P-3;" }, |
| 681 | {"(EX10.1,';')" , " -0X8.0P-3;" }, |
| 682 | {"(EX10.0,';')" , "-0X8.08P-3;" }, |
| 683 | {"(EX0.0,';')" , "-0X8.08P-3;" }, |
| 684 | {"(EX0,';')" , "-0X8.08P-3;" }, |
| 685 | {"(RN,EX10.1,';')" , " -0X8.0P-3;" }, |
| 686 | {"(RU,EX10.1,';')" , " -0X8.0P-3;" }, |
| 687 | {"(RD,EX10.1,';')" , " -0X8.1P-3;" }, |
| 688 | {"(RZ,EX10.1,';')" , " -0X8.0P-3;" }, |
| 689 | {"(RC,EX10.1,';')" , " -0X8.1P-3;" }, |
| 690 | {"(RN,EX10.0,';')" , "-0X8.08P-3;" }, |
| 691 | {"(RU,EX10.0,';')" , "-0X8.08P-3;" }, |
| 692 | {"(RD,EX10.0,';')" , "-0X8.08P-3;" }, |
| 693 | {"(RZ,EX10.0,';')" , "-0X8.08P-3;" }, |
| 694 | {"(RC,EX10.0,';')" , "-0X8.08P-3;" }, |
| 695 | }}, |
| 696 | }; |
| 697 | |
| 698 | for (auto const &[value, cases] : testCases) { |
| 699 | for (auto const &[format, expect] : cases) { |
| 700 | std::string got; |
| 701 | ASSERT_TRUE(CompareFormatReal(format, value, expect, got)) |
| 702 | << "Failed to format " << format << ", expected '" << expect |
| 703 | << "', got '" << got << "'" ; |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | using IndividualTestCaseTy = std::tuple<const char *, double, const char *>; |
| 708 | static const std::vector<IndividualTestCaseTy> individualTestCases{ |
| 709 | {"(F5.3,';')" , 25., "*****;" }, |
| 710 | {"(F5.3,';')" , 2.5, "2.500;" }, |
| 711 | {"(F5.3,';')" , 0.25, "0.250;" }, |
| 712 | {"(F5.3,';')" , 0.025, "0.025;" }, |
| 713 | {"(F5.3,';')" , 0.0025, "0.003;" }, |
| 714 | {"(F5.3,';')" , 0.00025, "0.000;" }, |
| 715 | {"(F5.3,';')" , 0.000025, "0.000;" }, |
| 716 | {"(F5.3,';')" , -25., "*****;" }, |
| 717 | {"(F5.3,';')" , -2.5, "*****;" }, |
| 718 | {"(F5.3,';')" , -0.25, "-.250;" }, |
| 719 | {"(F5.3,';')" , -0.025, "-.025;" }, |
| 720 | {"(F5.3,';')" , -0.0025, "-.003;" }, |
| 721 | {"(F5.3,';')" , -0.00025, "-.000;" }, |
| 722 | {"(F5.3,';')" , -0.000025, "-.000;" }, |
| 723 | {"(F5.3,';')" , 99.999, "*****;" }, |
| 724 | {"(F5.3,';')" , 9.9999, "*****;" }, |
| 725 | {"(F5.3,';')" , 0.99999, "1.000;" }, |
| 726 | {"(F5.3,';')" , 0.099999, "0.100;" }, |
| 727 | {"(F5.3,';')" , 0.0099999, "0.010;" }, |
| 728 | {"(F5.3,';')" , 0.00099999, "0.001;" }, |
| 729 | {"(F5.3,';')" , |
| 730 | 0.0005000000000000000104083408558608425664715468883514404296875, |
| 731 | "0.001;" }, |
| 732 | {"(F5.3,';')" , |
| 733 | 0.000499999999999999901988123607310399165726266801357269287109375, |
| 734 | "0.000;" }, |
| 735 | {"(F5.3,';')" , 0.000099999, "0.000;" }, |
| 736 | {"(F5.3,';')" , -99.999, "*****;" }, |
| 737 | {"(F5.3,';')" , -9.9999, "*****;" }, |
| 738 | {"(F5.3,';')" , -0.99999, "*****;" }, |
| 739 | {"(F5.3,';')" , -0.099999, "-.100;" }, |
| 740 | {"(F5.3,';')" , -0.0099999, "-.010;" }, |
| 741 | {"(F5.3,';')" , -0.00099999, "-.001;" }, |
| 742 | {"(F5.3,';')" , |
| 743 | -0.0005000000000000000104083408558608425664715468883514404296875, |
| 744 | "-.001;" }, |
| 745 | {"(F5.3,';')" , |
| 746 | -0.000499999999999999901988123607310399165726266801357269287109375, |
| 747 | "-.000;" }, |
| 748 | {"(F5.3,';')" , -0.000099999, "-.000;" }, |
| 749 | {"(F0.1,';')" , 0.0, ".0;" }, |
| 750 | {"(F5.0,';')" , -0.5000000000000001, " -1.;" }, |
| 751 | {"(F5.0,';')" , -0.5, " -0.;" }, |
| 752 | {"(F5.0,';')" , -0.49999999999999994, " -0.;" }, |
| 753 | {"(F5.0,';')" , 0.49999999999999994, " 0.;" }, |
| 754 | {"(F5.0,';')" , 0.5, " 0.;" }, |
| 755 | {"(F5.0,';')" , 0.5000000000000001, " 1.;" }, |
| 756 | {"(ES0.0E0,';')" , 0.666, "7.E-1;" }, |
| 757 | {"(EN0.0E0,';')" , 0.666, "666.E-3;" }, |
| 758 | }; |
| 759 | |
| 760 | for (auto const &[format, value, expect] : individualTestCases) { |
| 761 | std::string got; |
| 762 | char hex[17]; |
| 763 | std::snprintf(s: hex, maxlen: sizeof hex, format: "%016llx" , |
| 764 | *reinterpret_cast<const unsigned long long *>(&value)); |
| 765 | ASSERT_TRUE(CompareFormatReal(format, value, expect, got)) |
| 766 | << "Failed to format " << value << " 0x" << hex << " with format " |
| 767 | << format << ", expected '" << expect << "', got '" << got << "'" ; |
| 768 | } |
| 769 | |
| 770 | // Problematic EN formatting edge cases with rounding |
| 771 | using IndividualENTestCaseTy = std::tuple<std::uint64_t, const char *>; |
| 772 | static const std::vector<IndividualENTestCaseTy> individualENTestCases{ |
| 773 | {0x3E11183197785F8C, " 995.0E-12" }, // 0.9950312500000000115852E-09 |
| 774 | {0x3E11180E68455D30, " 995.0E-12" }, // 0.9949999999999999761502E-09 |
| 775 | {0x3E112BD8F4F6B0D7, " 999.5E-12" }, // 0.9994999999999999089118E-09 |
| 776 | {0x3E45794883CA8782, " 10.0E-09" }, // 0.9999499999999999642266E-08 |
| 777 | {0x3F506218230C7482, " 999.9E-06" }, // 0.9999499999999998840761E-03 |
| 778 | {0x3FB99652BD3C3612, " 100.0E-03" }, // 0.9999500000000000055067E+00 |
| 779 | {0x4023E66666666667, " 10.0E+00" }, // 0.9950000000000001065814E+01 |
| 780 | }; |
| 781 | |
| 782 | for (auto const &[value, expect] : individualENTestCases) { |
| 783 | std::string got; |
| 784 | ASSERT_TRUE(CompareFormatReal("(EN10.1)" , value, expect, got)) |
| 785 | << "Failed to format EN10.1, expected '" << expect << "', got '" << got |
| 786 | << "'" ; |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | TEST(IOApiTests, FormatIntegerValues) { |
| 791 | using IntTestCaseTy = std::tuple<const char *, std::int64_t, const char *>; |
| 792 | static const std::vector<IntTestCaseTy> intTestCases{ |
| 793 | {"(I4)" , 0, " 0" }, |
| 794 | {"(I4)" , 1, " 1" }, |
| 795 | {"(I4)" , 9999, "9999" }, |
| 796 | {"(SP,I4)" , 1, " +1" }, |
| 797 | {"(SP,I4)" , 9999, "****" }, |
| 798 | {"(SP,I4)" , 999, "+999" }, |
| 799 | {"(I4)" , -1, " -1" }, |
| 800 | {"(I4)" , -9999, "****" }, |
| 801 | {"(I4)" , -999, "-999" }, |
| 802 | {"(I4.2)" , 1, " 01" }, |
| 803 | {"(I4.2)" , -1, " -01" }, |
| 804 | {"(I4.2)" , 999, " 999" }, |
| 805 | {"(I4.4)" , 999, "0999" }, |
| 806 | {"(I0)" , 0, "0" }, |
| 807 | {"(I0)" , 1, "1" }, |
| 808 | {"(I0)" , 9999, "9999" }, |
| 809 | {"(SP,I0)" , 1, "+1" }, |
| 810 | {"(SP,I0)" , 9999, "+9999" }, |
| 811 | {"(SP,I0)" , 999, "+999" }, |
| 812 | {"(I0)" , -1, "-1" }, |
| 813 | {"(I0)" , -9999, "-9999" }, |
| 814 | {"(I0)" , -999, "-999" }, |
| 815 | {"(I0.2)" , 1, "01" }, |
| 816 | {"(I0.2)" , -1, "-01" }, |
| 817 | {"(I0.2)" , 999, "999" }, |
| 818 | {"(I0.4)" , 999, "0999" }, |
| 819 | {"(G4)" , 0, " 0" }, |
| 820 | {"(G4)" , 1, " 1" }, |
| 821 | {"(G4)" , 9999, "9999" }, |
| 822 | {"(SP,G4)" , 1, " +1" }, |
| 823 | {"(SP,G4)" , 9999, "****" }, |
| 824 | {"(SP,G4)" , 999, "+999" }, |
| 825 | {"(G4)" , -1, " -1" }, |
| 826 | {"(G4)" , -9999, "****" }, |
| 827 | {"(G4)" , -999, "-999" }, |
| 828 | {"(G4.2)" , 1, " 1" }, |
| 829 | {"(G4.2)" , -1, " -1" }, |
| 830 | {"(G4.2)" , 999, " 999" }, |
| 831 | {"(G4.4)" , 999, " 999" }, |
| 832 | {"(G0)" , 0, "0" }, |
| 833 | {"(G0)" , 1, "1" }, |
| 834 | {"(G0)" , 9999, "9999" }, |
| 835 | {"(SP,G0)" , 1, "+1" }, |
| 836 | {"(SP,G0)" , 9999, "+9999" }, |
| 837 | {"(SP,G0)" , 999, "+999" }, |
| 838 | {"(G0)" , -1, "-1" }, |
| 839 | {"(G0)" , -9999, "-9999" }, |
| 840 | {"(G0)" , -999, "-999" }, |
| 841 | {"(G0.2)" , 1, "1" }, |
| 842 | {"(G0.2)" , -1, "-1" }, |
| 843 | {"(G0.2)" , 999, "999" }, |
| 844 | {"(G0.4)" , 999, "999" }, |
| 845 | {"(I)" , 999, "999" }, |
| 846 | {"(G)" , 999, "999" }, |
| 847 | {"('x',I)" , 999, "x 999" }, |
| 848 | {"('x',G)" , 999, "x 999" }, |
| 849 | }; |
| 850 | |
| 851 | for (auto const &[fmt, value, expect] : intTestCases) { |
| 852 | std::string got; |
| 853 | ASSERT_TRUE(CompareFormatInteger(fmt, value, expect, got)) |
| 854 | << "Failed to format " << fmt << ", expected '" << expect << "', got '" |
| 855 | << got << "'" ; |
| 856 | } |
| 857 | } |
| 858 | |
| 859 | //------------------------------------------------------------------------------ |
| 860 | /// Tests for input editing real values |
| 861 | //------------------------------------------------------------------------------ |
| 862 | |
| 863 | // Ensure double input values correctly map to raw uint64 values |
| 864 | TEST(IOApiTests, EditDoubleInputValues) { |
| 865 | using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t, int>; |
| 866 | int ovf{IostatRealInputOverflow}; |
| 867 | static const std::vector<TestCaseTy> testCases{ |
| 868 | {"(F18.0)" , " 0" , 0x0, 0}, |
| 869 | {"(F18.0)" , " " , 0x0, 0}, |
| 870 | {"(F18.0)" , " -0" , 0x8000000000000000, 0}, |
| 871 | {"(F18.0)" , " 01" , 0x3ff0000000000000, 0}, |
| 872 | {"(F18.0)" , " 1" , 0x3ff0000000000000, 0}, |
| 873 | {"(F18.0)" , " 125." , 0x405f400000000000, 0}, |
| 874 | {"(F18.0)" , " 12.5" , 0x4029000000000000, 0}, |
| 875 | {"(F18.0)" , " 1.25" , 0x3ff4000000000000, 0}, |
| 876 | {"(F18.0)" , " 01.25" , 0x3ff4000000000000, 0}, |
| 877 | {"(F18.0)" , " .125" , 0x3fc0000000000000, 0}, |
| 878 | {"(F18.0)" , " 0.125" , 0x3fc0000000000000, 0}, |
| 879 | {"(F18.0)" , " .0625" , 0x3fb0000000000000, 0}, |
| 880 | {"(F18.0)" , " 0.0625" , 0x3fb0000000000000, 0}, |
| 881 | {"(F18.0)" , " 125" , 0x405f400000000000, 0}, |
| 882 | {"(F18.1)" , " 125" , 0x4029000000000000, 0}, |
| 883 | {"(F18.2)" , " 125" , 0x3ff4000000000000, 0}, |
| 884 | {"(F18.3)" , " 125" , 0x3fc0000000000000, 0}, |
| 885 | {"('str',F3.0)" , "xxx125" , 0x405f400000000000, 0}, |
| 886 | {"(-1P,F18.0)" , " 125" , 0x4093880000000000, 0}, // 1250 |
| 887 | {"(1P,F18.0)" , " 125" , 0x4029000000000000, 0}, // 12.5 |
| 888 | {"(BZ,F18.0)" , " 125 " , 0x4093880000000000, 0}, // 1250 |
| 889 | {"(BZ,F18.0)" , " 125 . e +1 " , 0x42a6bcc41e900000, 0}, // 1.25e13 |
| 890 | {"(BZ,F18.0)" , " . " , 0x0, 0}, |
| 891 | {"(BZ,F18.0)" , " . e +1 " , 0x0, 0}, |
| 892 | {"(DC,F18.0)" , " 12,5" , 0x4029000000000000, 0}, |
| 893 | {"(EX22.0)" , "0X0P0 " , 0x0, 0}, // +0. |
| 894 | {"(EX22.0)" , "-0X0P0 " , 0x8000000000000000, 0}, // -0. |
| 895 | {"(EX22.0)" , "0X.8P1 " , 0x3ff0000000000000, 0}, // 1.0 |
| 896 | {"(EX22.0)" , "0X8.P-3 " , 0x3ff0000000000000, 0}, // 1.0 |
| 897 | {"(EX22.0)" , "0X.1P4 " , 0x3ff0000000000000, 0}, // 1.0 |
| 898 | {"(EX22.0)" , "0X10.P-4 " , 0x3ff0000000000000, 0}, // 1.0 |
| 899 | {"(EX22.0)" , "0X8.00P-3 " , 0x3ff0000000000000, 0}, // 1.0 |
| 900 | {"(EX22.0)" , "0X80.0P-6 " , 0x4000000000000000, 0}, // 2.0 |
| 901 | {"(EX22.0)" , "0XC.CCCCCCCCCCCDP-7 " , 0x3fb999999999999a, 0}, // 0.1 |
| 902 | {"(EX22.0)" , "0X.8P-1021 " , 0x0010000000000000, |
| 903 | 0}, // min normal |
| 904 | {"(EX22.0)" , "0X.8P-1022 " , 0x0008000000000000, |
| 905 | 0}, // subnormal |
| 906 | {"(EX22.0)" , "0X.8P-1073 " , 0x0000000000000001, |
| 907 | 0}, // min subn. |
| 908 | {"(EX22.0)" , "0X.FFFFFFFFFFFFF8P1024" , 0x7fefffffffffffff, |
| 909 | 0}, // max finite |
| 910 | {"(EX22.0)" , "0X.8P1025 " , 0x7ff0000000000000, ovf}, // +Inf |
| 911 | {"(EX22.0)" , "-0X.8P1025 " , 0xfff0000000000000, ovf}, // -Inf |
| 912 | {"(RC,EX22.0)" , "0X1.0000000000000P0 " , 0x3ff0000000000000, 0}, |
| 913 | {"(RC,EX22.0)" , "0X1.00000000000008P0 " , 0x3ff0000000000001, 0}, |
| 914 | {"(RC,EX22.0)" , "0X1.000000000000008P0 " , 0x3ff0000000000000, 0}, |
| 915 | {"(RC,EX22.0)" , "0X1.00000000000004P0 " , 0x3ff0000000000000, 0}, |
| 916 | {"(RC,EX22.0)" , "0X.80000000000000P1 " , 0x3ff0000000000000, 0}, |
| 917 | {"(RC,EX22.0)" , "0X.80000000000004P1 " , 0x3ff0000000000001, 0}, |
| 918 | {"(RC,EX22.0)" , "0X.800000000000004P1 " , 0x3ff0000000000000, 0}, |
| 919 | {"(RC,EX22.0)" , "0X.80000000000002P1 " , 0x3ff0000000000000, 0}, |
| 920 | {"(RZ,F7.0)" , " 2.e308" , 0x7fefffffffffffff, 0}, // +HUGE() |
| 921 | {"(RD,F7.0)" , " 2.e308" , 0x7fefffffffffffff, 0}, // +HUGE() |
| 922 | {"(RU,F7.0)" , " 2.e308" , 0x7ff0000000000000, ovf}, // +Inf |
| 923 | {"(RZ,F7.0)" , "-2.e308" , 0xffefffffffffffff, 0}, // -HUGE() |
| 924 | {"(RD,F7.0)" , "-2.e308" , 0xfff0000000000000, ovf}, // -Inf |
| 925 | {"(RU,F7.0)" , "-2.e308" , 0xffefffffffffffff, 0}, // -HUGE() |
| 926 | {"(RZ,F7.0)" , " 1.e999" , 0x7fefffffffffffff, 0}, // +HUGE() |
| 927 | {"(RD,F7.0)" , " 1.e999" , 0x7fefffffffffffff, 0}, // +HUGE() |
| 928 | {"(RU,F7.0)" , " 1.e999" , 0x7ff0000000000000, ovf}, // +Inf |
| 929 | {"(RZ,F7.0)" , "-1.e999" , 0xffefffffffffffff, 0}, // -HUGE() |
| 930 | {"(RD,F7.0)" , "-1.e999" , 0xfff0000000000000, ovf}, // -Inf |
| 931 | {"(RU,F7.0)" , "-1.e999" , 0xffefffffffffffff, 0}, // -HUGE() |
| 932 | {"(E9.1)" , " 1.0E-325" , 0x0, 0}, |
| 933 | {"(RU,E9.1)" , " 1.0E-325" , 0x1, 0}, |
| 934 | {"(E9.1)" , "-1.0E-325" , 0x8000000000000000, 0}, |
| 935 | {"(RD,E9.1)" , "-1.0E-325" , 0x8000000000000001, 0}, |
| 936 | }; |
| 937 | for (auto const &[format, data, want, iostat] : testCases) { |
| 938 | auto cookie{IONAME(BeginInternalFormattedInput)( |
| 939 | data, std::strlen(data), format, std::strlen(format))}; |
| 940 | union { |
| 941 | double x; |
| 942 | std::uint64_t raw; |
| 943 | } u; |
| 944 | u.raw = 0; |
| 945 | |
| 946 | // Read buffer into union value |
| 947 | IONAME(EnableHandlers)(cookie, true, true, true, true, true); |
| 948 | IONAME(InputReal64)(cookie, u.x); |
| 949 | |
| 950 | static constexpr int bufferSize{65}; |
| 951 | char iomsg[bufferSize]; |
| 952 | std::memset(s: iomsg, c: '\0', n: bufferSize - 1); |
| 953 | |
| 954 | // Ensure no unexpected errors were encountered reading input buffer into |
| 955 | // union value |
| 956 | IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); |
| 957 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 958 | ASSERT_EQ(status, iostat) |
| 959 | << '\'' << format << "' failed reading '" << data << "', status " |
| 960 | << static_cast<int>(status) << " != expected " << iostat << " iomsg '" |
| 961 | << iomsg << "'" ; |
| 962 | |
| 963 | // Ensure raw uint64 value matches expected conversion from double |
| 964 | ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data |
| 965 | << "', want " << want << ", got " << u.raw; |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | // regression test for confusing digit minimization |
| 970 | TEST(IOApiTests, ConfusingMinimization) { |
| 971 | char buffer[8]{}; |
| 972 | auto cookie{IONAME(BeginInternalListOutput)(buffer, sizeof buffer)}; |
| 973 | StaticDescriptor<0> staticDescriptor; |
| 974 | Descriptor &desc{staticDescriptor.descriptor()}; |
| 975 | std::uint16_t x{0x7bff}; // HUGE(0._2) |
| 976 | desc.Establish(TypeCode{CFI_type_half_float}, sizeof x, &x, 0, nullptr); |
| 977 | desc.Check(); |
| 978 | EXPECT_TRUE(IONAME(OutputDescriptor)(cookie, desc)); |
| 979 | auto status{IONAME(EndIoStatement)(cookie)}; |
| 980 | EXPECT_EQ(status, 0); |
| 981 | std::string got{std::string{buffer, sizeof buffer}}; |
| 982 | EXPECT_TRUE(CompareFormattedStrings(" 65504. " , got)) |
| 983 | << "expected ' 65504. ', got '" << got << '\''; // not 65500.! |
| 984 | } |
| 985 | |