1 //===-- runtime/edit-output.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "edit-output.h"
10 #include "emit-encoded.h"
12 #include "flang/Common/uint128.h"
15 namespace Fortran::runtime::io
{
17 // In output statement, add a space between numbers and characters.
18 static void addSpaceBeforeCharacter(IoStatementState
&io
) {
19 if (auto *list
{io
.get_if
<ListDirectedStatementState
<Direction::Output
>>()}) {
20 list
->set_lastWasUndelimitedCharacter(false);
24 // B/O/Z output of arbitrarily sized data emits a binary/octal/hexadecimal
25 // representation of what is interpreted to be a single unsigned integer value.
26 // When used with character data, endianness is exposed.
27 template <int LOG2_BASE
>
28 static bool EditBOZOutput(IoStatementState
&io
, const DataEdit
&edit
,
29 const unsigned char *data0
, std::size_t bytes
) {
30 addSpaceBeforeCharacter(io
);
31 int digits
{static_cast<int>((bytes
* 8) / LOG2_BASE
)};
32 int get
{static_cast<int>(bytes
* 8) - digits
* LOG2_BASE
};
39 int increment
{isHostLittleEndian
? -1 : 1};
40 const unsigned char *data
{data0
+ (isHostLittleEndian
? bytes
- 1 : 0)};
43 // The same algorithm is used to generate digits for real (below)
44 // as well as for generating them only to skip leading zeroes (here).
45 // Bits are copied one at a time from the source data.
46 // TODO: Multiple bit copies for hexadecimal, where misalignment
47 // is not possible; or for octal when all 3 bits come from the
52 break; // first nonzero leading digit
56 } else if (shift
< 0) {
61 digit
= 2 * digit
+ ((*data
>> shift
--) & 1);
65 // Emit leading spaces and zeroes; detect field overflow
67 int editWidth
{edit
.width
.value_or(0)};
68 int significant
{digits
- skippedZeroes
};
69 if (edit
.digits
&& significant
<= *edit
.digits
) { // Bw.m, Ow.m, Zw.m
70 if (*edit
.digits
== 0 && bytes
== 0) {
71 editWidth
= std::max(1, editWidth
);
73 leadingZeroes
= *edit
.digits
- significant
;
75 } else if (bytes
== 0) {
78 int subTotal
{leadingZeroes
+ significant
};
79 int leadingSpaces
{std::max(0, editWidth
- subTotal
)};
80 if (editWidth
> 0 && leadingSpaces
+ subTotal
> editWidth
) {
81 return EmitRepeated(io
, '*', editWidth
);
83 if (!(EmitRepeated(io
, ' ', leadingSpaces
) &&
84 EmitRepeated(io
, '0', leadingZeroes
))) {
87 // Emit remaining digits
90 char ch
{static_cast<char>(digit
>= 10 ? 'A' + digit
- 10 : '0' + digit
)};
91 if (!EmitAscii(io
, &ch
, 1)) {
96 } else if (shift
< 0) {
101 digit
= 2 * digit
+ ((*data
>> shift
--) & 1);
109 bool EditIntegerOutput(IoStatementState
&io
, const DataEdit
&edit
,
110 common::HostSignedIntType
<8 * KIND
> n
) {
111 addSpaceBeforeCharacter(io
);
112 char buffer
[130], *end
{&buffer
[sizeof buffer
]}, *p
{end
};
113 bool isNegative
{n
< 0};
114 using Unsigned
= common::HostUnsignedIntType
<8 * KIND
>;
115 Unsigned un
{static_cast<Unsigned
>(n
)};
117 switch (edit
.descriptor
) {
118 case DataEdit::ListDirected
:
124 if (isNegative
|| (edit
.modes
.editingFlags
& signPlus
)) {
125 signChars
= 1; // '-' or '+'
128 auto quotient
{un
/ 10u};
129 *--p
= '0' + static_cast<int>(un
- Unsigned
{10} * quotient
);
134 return EditBOZOutput
<1>(
135 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
137 return EditBOZOutput
<3>(
138 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
140 return EditBOZOutput
<4>(
141 io
, edit
, reinterpret_cast<const unsigned char *>(&n
), KIND
);
143 return EditLogicalOutput(io
, edit
, n
!= 0 ? true : false);
144 case 'A': // legacy extension
145 return EditCharacterOutput(
146 io
, edit
, reinterpret_cast<char *>(&n
), sizeof n
);
148 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
149 "Data edit descriptor '%c' may not be used with an INTEGER data item",
154 int digits
= end
- p
;
155 int leadingZeroes
{0};
156 int editWidth
{edit
.width
.value_or(0)};
157 if (edit
.descriptor
== 'I' && edit
.digits
&& digits
<= *edit
.digits
) {
158 // Only Iw.m can produce leading zeroes, not Gw.d (F'202X 13.7.5.2.2)
159 if (*edit
.digits
== 0 && n
== 0) {
160 // Iw.0 with zero value: output field must be blank. For I0.0
161 // and a zero value, emit one blank character.
162 signChars
= 0; // in case of SP
163 editWidth
= std::max(1, editWidth
);
165 leadingZeroes
= *edit
.digits
- digits
;
170 int subTotal
{signChars
+ leadingZeroes
+ digits
};
171 int leadingSpaces
{std::max(0, editWidth
- subTotal
)};
172 if (editWidth
> 0 && leadingSpaces
+ subTotal
> editWidth
) {
173 return EmitRepeated(io
, '*', editWidth
);
175 if (edit
.IsListDirected()) {
176 int total
{std::max(leadingSpaces
, 1) + subTotal
};
177 if (io
.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total
)) &&
178 !io
.AdvanceRecord()) {
183 return EmitRepeated(io
, ' ', leadingSpaces
) &&
184 EmitAscii(io
, n
< 0 ? "-" : "+", signChars
) &&
185 EmitRepeated(io
, '0', leadingZeroes
) && EmitAscii(io
, p
, digits
);
188 // Formats the exponent (see table 13.1 for all the cases)
189 const char *RealOutputEditingBase::FormatExponent(
190 int expo
, const DataEdit
&edit
, int &length
) {
191 char *eEnd
{&exponent_
[sizeof exponent_
]};
192 char *exponent
{eEnd
};
193 for (unsigned e
{static_cast<unsigned>(std::abs(expo
))}; e
> 0;) {
194 unsigned quotient
{e
/ 10u};
195 *--exponent
= '0' + e
- 10 * quotient
;
198 bool overflow
{false};
199 if (edit
.expoDigits
) {
200 if (int ed
{*edit
.expoDigits
}) { // Ew.dEe with e > 0
201 overflow
= exponent
+ ed
< eEnd
;
202 while (exponent
> exponent_
+ 2 /*E+*/ && exponent
+ ed
> eEnd
) {
205 } else if (exponent
== eEnd
) {
206 *--exponent
= '0'; // Ew.dE0 with zero-valued exponent
208 } else if (edit
.variation
== 'X') {
210 *--exponent
= '0'; // EX without Ee and zero-valued exponent
213 // Ensure at least two exponent digits unless EX
214 while (exponent
+ 2 > eEnd
) {
218 *--exponent
= expo
< 0 ? '-' : '+';
219 if (edit
.variation
== 'X') {
221 } else if (edit
.expoDigits
|| edit
.IsListDirected() || exponent
+ 3 == eEnd
) {
222 *--exponent
= edit
.descriptor
== 'D' ? 'D' : 'E'; // not 'G' or 'Q'
224 length
= eEnd
- exponent
;
225 return overflow
? nullptr : exponent
;
228 bool RealOutputEditingBase::EmitPrefix(
229 const DataEdit
&edit
, std::size_t length
, std::size_t width
) {
230 if (edit
.IsListDirected()) {
231 int prefixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
? 2
232 : edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
? 0
234 int suffixLength
{edit
.descriptor
== DataEdit::ListDirectedRealPart
||
235 edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
238 length
+= prefixLength
+ suffixLength
;
239 ConnectionState
&connection
{io_
.GetConnectionState()};
240 return (!connection
.NeedAdvance(length
) || io_
.AdvanceRecord()) &&
241 EmitAscii(io_
, " (", prefixLength
);
242 } else if (width
> length
) {
243 return EmitRepeated(io_
, ' ', width
- length
);
249 bool RealOutputEditingBase::EmitSuffix(const DataEdit
&edit
) {
250 if (edit
.descriptor
== DataEdit::ListDirectedRealPart
) {
252 io_
, edit
.modes
.editingFlags
& decimalComma
? ";" : ",", 1);
253 } else if (edit
.descriptor
== DataEdit::ListDirectedImaginaryPart
) {
254 return EmitAscii(io_
, ")", 1);
261 decimal::ConversionToDecimalResult RealOutputEditing
<KIND
>::ConvertToDecimal(
262 int significantDigits
, enum decimal::FortranRounding rounding
, int flags
) {
263 auto converted
{decimal::ConvertToDecimal
<binaryPrecision
>(buffer_
,
264 sizeof buffer_
, static_cast<enum decimal::DecimalConversionFlags
>(flags
),
265 significantDigits
, rounding
, x_
)};
266 if (!converted
.str
) { // overflow
267 io_
.GetIoErrorHandler().Crash(
268 "RealOutputEditing::ConvertToDecimal: buffer size %zd was insufficient",
274 static bool IsInfOrNaN(const char *p
, int length
) {
275 if (!p
|| length
< 1) {
278 if (*p
== '-' || *p
== '+') {
284 return *p
== 'I' || *p
== 'N';
287 // 13.7.2.3.3 in F'2018
289 bool RealOutputEditing
<KIND
>::EditEorDOutput(const DataEdit
&edit
) {
290 addSpaceBeforeCharacter(io_
);
291 int editDigits
{edit
.digits
.value_or(0)}; // 'd' field
292 int editWidth
{edit
.width
.value_or(0)}; // 'w' field
293 int significantDigits
{editDigits
};
295 if (edit
.modes
.editingFlags
& signPlus
) {
296 flags
|= decimal::AlwaysSign
;
298 int scale
{edit
.modes
.scale
}; // 'kP' value
299 if (editWidth
== 0) { // "the processor selects the field width"
300 if (edit
.digits
.has_value()) { // E0.d
301 if (editDigits
== 0 && scale
<= 0) { // E0.0
302 significantDigits
= 1;
305 flags
|= decimal::Minimize
;
307 sizeof buffer_
- 5; // sign, NUL, + 3 extra for EN scaling
310 bool isEN
{edit
.variation
== 'N'};
311 bool isES
{edit
.variation
== 'S'};
312 int zeroesAfterPoint
{0};
314 scale
= IsZero() ? 1 : 3;
315 significantDigits
+= scale
;
319 } else if (scale
< 0) {
320 if (scale
<= -editDigits
) {
321 io_
.GetIoErrorHandler().SignalError(IostatBadScaleFactor
,
322 "Scale factor (kP) %d cannot be less than -d (%d)", scale
,
326 zeroesAfterPoint
= -scale
;
327 significantDigits
= std::max(0, significantDigits
- zeroesAfterPoint
);
328 } else if (scale
> 0) {
329 if (scale
>= editDigits
+ 2) {
330 io_
.GetIoErrorHandler().SignalError(IostatBadScaleFactor
,
331 "Scale factor (kP) %d cannot be greater than d+2 (%d)", scale
,
336 scale
= std::min(scale
, significantDigits
+ 1);
338 // In EN editing, multiple attempts may be necessary, so this is a loop.
340 decimal::ConversionToDecimalResult converted
{
341 ConvertToDecimal(significantDigits
, edit
.modes
.round
, flags
)};
342 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
343 return editWidth
> 0 &&
344 converted
.length
+ trailingBlanks_
>
345 static_cast<std::size_t>(editWidth
)
346 ? EmitRepeated(io_
, '*', editWidth
)
347 : EmitPrefix(edit
, converted
.length
, editWidth
) &&
348 EmitAscii(io_
, converted
.str
, converted
.length
) &&
349 EmitRepeated(io_
, ' ', trailingBlanks_
) && EmitSuffix(edit
);
352 converted
.decimalExponent
-= scale
;
355 // EN mode: we need an effective exponent field that is
356 // a multiple of three.
357 if (int modulus
{converted
.decimalExponent
% 3}; modulus
!= 0) {
358 if (significantDigits
> 1) {
363 // Rounded nines up to a 1.
365 converted
.decimalExponent
-= modulus
;
368 int adjust
{3 * (scale
/ 3)};
370 converted
.decimalExponent
+= adjust
;
371 } else if (scale
< 1) {
372 int adjust
{3 - 3 * (scale
/ 3)};
374 converted
.decimalExponent
-= adjust
;
376 significantDigits
= editDigits
+ scale
;
378 // Format the exponent (see table 13.1 for all the cases)
380 const char *exponent
{
381 FormatExponent(converted
.decimalExponent
, edit
, expoLength
)};
382 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
383 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
384 int zeroesBeforePoint
{std::max(0, scale
- convertedDigits
)};
385 int digitsBeforePoint
{std::max(0, scale
- zeroesBeforePoint
)};
386 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
387 int trailingZeroes
{flags
& decimal::Minimize
390 significantDigits
- (convertedDigits
+ zeroesBeforePoint
))};
391 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
392 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingZeroes
+
394 int width
{editWidth
> 0 ? editWidth
: totalLength
};
395 if (totalLength
> width
|| !exponent
) {
396 return EmitRepeated(io_
, '*', width
);
398 if (totalLength
< width
&& digitsBeforePoint
== 0 &&
399 zeroesBeforePoint
== 0) {
400 zeroesBeforePoint
= 1;
403 if (totalLength
< width
&& editWidth
== 0) {
406 return EmitPrefix(edit
, totalLength
, width
) &&
407 EmitAscii(io_
, converted
.str
, signLength
+ digitsBeforePoint
) &&
408 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
409 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
410 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
411 EmitAscii(io_
, converted
.str
+ signLength
+ digitsBeforePoint
,
413 EmitRepeated(io_
, '0', trailingZeroes
) &&
414 EmitAscii(io_
, exponent
, expoLength
) && EmitSuffix(edit
);
418 // 13.7.2.3.2 in F'2018
420 bool RealOutputEditing
<KIND
>::EditFOutput(const DataEdit
&edit
) {
421 addSpaceBeforeCharacter(io_
);
422 int fracDigits
{edit
.digits
.value_or(0)}; // 'd' field
423 const int editWidth
{edit
.width
.value_or(0)}; // 'w' field
424 enum decimal::FortranRounding rounding
{edit
.modes
.round
};
426 if (edit
.modes
.editingFlags
& signPlus
) {
427 flags
|= decimal::AlwaysSign
;
429 if (editWidth
== 0) { // "the processor selects the field width"
430 if (!edit
.digits
.has_value()) { // F0
431 flags
|= decimal::Minimize
;
432 fracDigits
= sizeof buffer_
- 2; // sign & NUL
435 // Multiple conversions may be needed to get the right number of
436 // effective rounded fractional digits.
437 bool canIncrease
{true};
438 for (int extraDigits
{fracDigits
== 0 ? 1 : 0};;) {
439 decimal::ConversionToDecimalResult converted
{
440 ConvertToDecimal(extraDigits
+ fracDigits
, rounding
, flags
)};
441 const char *convertedStr
{converted
.str
};
442 if (IsInfOrNaN(convertedStr
, static_cast<int>(converted
.length
))) {
443 return editWidth
> 0 &&
444 converted
.length
> static_cast<std::size_t>(editWidth
)
445 ? EmitRepeated(io_
, '*', editWidth
)
446 : EmitPrefix(edit
, converted
.length
, editWidth
) &&
447 EmitAscii(io_
, convertedStr
, converted
.length
) &&
450 int expo
{converted
.decimalExponent
+ edit
.modes
.scale
/*kP*/};
451 int signLength
{*convertedStr
== '-' || *convertedStr
== '+' ? 1 : 0};
452 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
453 if (IsZero()) { // don't treat converted "0" as significant digit
457 bool isNegative
{*convertedStr
== '-'};
459 if (expo
> extraDigits
&& extraDigits
>= 0 && canIncrease
) {
461 if (!edit
.digits
.has_value()) { // F0
462 fracDigits
= sizeof buffer_
- extraDigits
- 2; // sign & NUL
464 canIncrease
= false; // only once
466 } else if (expo
== -fracDigits
&& convertedDigits
> 0) {
467 // Result will be either a signed zero or power of ten, depending
469 char leading
{convertedStr
[signLength
]};
470 bool roundToPowerOfTen
{false};
471 switch (edit
.modes
.round
) {
472 case decimal::FortranRounding::RoundUp
:
473 roundToPowerOfTen
= !isNegative
;
475 case decimal::FortranRounding::RoundDown
:
476 roundToPowerOfTen
= isNegative
;
478 case decimal::FortranRounding::RoundToZero
:
480 case decimal::FortranRounding::RoundNearest
:
481 if (leading
== '5' &&
482 rounding
== decimal::FortranRounding::RoundNearest
) {
483 // Try again, rounding away from zero.
484 rounding
= isNegative
? decimal::FortranRounding::RoundDown
485 : decimal::FortranRounding::RoundUp
;
486 extraDigits
= 1 - fracDigits
; // just one digit needed
489 roundToPowerOfTen
= leading
> '5';
491 case decimal::FortranRounding::RoundCompatible
:
492 roundToPowerOfTen
= leading
>= '5';
495 if (roundToPowerOfTen
) {
498 if (signLength
> 0) {
499 one
[0] = *convertedStr
;
509 } else if (expo
< extraDigits
&& extraDigits
> -fracDigits
) {
510 extraDigits
= std::max(expo
, -fracDigits
);
513 int digitsBeforePoint
{std::max(0, std::min(expo
, convertedDigits
))};
514 int zeroesBeforePoint
{std::max(0, expo
- digitsBeforePoint
)};
515 int zeroesAfterPoint
{std::min(fracDigits
, std::max(0, -expo
))};
516 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
517 int trailingZeroes
{flags
& decimal::Minimize
519 : std::max(0, fracDigits
- (zeroesAfterPoint
+ digitsAfterPoint
))};
520 if (digitsBeforePoint
+ zeroesBeforePoint
+ zeroesAfterPoint
+
521 digitsAfterPoint
+ trailingZeroes
==
523 zeroesBeforePoint
= 1; // "." -> "0."
525 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
526 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingZeroes
+
527 trailingBlanks_
/* G editing converted to F */};
528 int width
{editWidth
> 0 || trailingBlanks_
? editWidth
: totalLength
};
529 if (totalLength
> width
) {
530 return EmitRepeated(io_
, '*', width
);
532 if (totalLength
< width
&& digitsBeforePoint
+ zeroesBeforePoint
== 0) {
533 zeroesBeforePoint
= 1;
536 return EmitPrefix(edit
, totalLength
, width
) &&
537 EmitAscii(io_
, convertedStr
, signLength
+ digitsBeforePoint
) &&
538 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
539 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
540 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
541 EmitAscii(io_
, convertedStr
+ signLength
+ digitsBeforePoint
,
543 EmitRepeated(io_
, '0', trailingZeroes
) &&
544 EmitRepeated(io_
, ' ', trailingBlanks_
) && EmitSuffix(edit
);
548 // 13.7.5.2.3 in F'2018
550 DataEdit RealOutputEditing
<KIND
>::EditForGOutput(DataEdit edit
) {
551 edit
.descriptor
= 'E';
552 int editWidth
{edit
.width
.value_or(0)};
553 int significantDigits
{
554 edit
.digits
.value_or(BinaryFloatingPoint::decimalPrecision
)}; // 'd'
555 if (editWidth
> 0 && significantDigits
== 0) {
556 return edit
; // Gw.0Ee -> Ew.0Ee for w > 0
559 if (edit
.modes
.editingFlags
& signPlus
) {
560 flags
|= decimal::AlwaysSign
;
562 decimal::ConversionToDecimalResult converted
{
563 ConvertToDecimal(significantDigits
, edit
.modes
.round
, flags
)};
564 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
565 return edit
; // Inf/Nan -> Ew.d (same as Fw.d)
567 int expo
{IsZero() ? 1 : converted
.decimalExponent
}; // 's'
568 if (expo
< 0 || expo
> significantDigits
) {
569 if (editWidth
== 0 && !edit
.expoDigits
) { // G0.d -> G0.dE0
572 return edit
; // Ew.dEe
574 edit
.descriptor
= 'F';
575 edit
.modes
.scale
= 0; // kP is ignored for G when no exponent field
578 int expoDigits
{edit
.expoDigits
.value_or(0)};
579 // F'2023 13.7.5.2.3 p5: "If 0 <= s <= d, the scale factor has no effect
580 // and F(w − n).(d − s),n(’b’) editing is used where b is a blank and
581 // n is 4 for Gw.d editing, e + 2 for Gw.dEe editing if e > 0, and
582 // 4 for Gw.dE0 editing."
583 trailingBlanks_
= expoDigits
> 0 ? expoDigits
+ 2 : 4; // 'n'
585 if (edit
.digits
.has_value()) {
586 *edit
.digits
= std::max(0, *edit
.digits
- expo
);
593 bool RealOutputEditing
<KIND
>::EditListDirectedOutput(const DataEdit
&edit
) {
594 decimal::ConversionToDecimalResult converted
{
595 ConvertToDecimal(1, edit
.modes
.round
)};
596 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
597 return EditEorDOutput(edit
);
599 int expo
{converted
.decimalExponent
};
600 // The decimal precision of 16-bit floating-point types is very low,
601 // so use a reasonable cap of 6 to allow more values to be emitted
602 // with Fw.d editing.
603 static constexpr int maxExpo
{
604 std::max(6, BinaryFloatingPoint::decimalPrecision
)};
605 if (expo
< 0 || expo
> maxExpo
) {
607 copy
.modes
.scale
= 1; // 1P
608 return EditEorDOutput(copy
);
610 return EditFOutput(edit
);
613 // 13.7.2.3.6 in F'2023
614 // The specification for hexadecimal output, unfortunately for implementors,
615 // leaves as "implementation dependent" the choice of how to emit values
616 // with multiple hexadecimal output possibilities that are numerically
617 // equivalent. The one working implementation of EX output that I can find
618 // apparently chooses to frame the nybbles from most to least significant,
619 // rather than trying to minimize the magnitude of the binary exponent.
620 // E.g., 2. is edited into 0X8.0P-2 rather than 0X2.0P0. This implementation
621 // follows that precedent so as to avoid a gratuitous incompatibility.
623 auto RealOutputEditing
<KIND
>::ConvertToHexadecimal(
624 int significantDigits
, enum decimal::FortranRounding rounding
, int flags
)
625 -> ConvertToHexadecimalResult
{
626 if (x_
.IsNaN() || x_
.IsInfinite()) {
627 auto converted
{ConvertToDecimal(significantDigits
, rounding
, flags
)};
628 return {converted
.str
, static_cast<int>(converted
.length
), 0};
630 x_
.RoundToBits(4 * significantDigits
, rounding
);
631 if (x_
.IsInfinite()) { // rounded away to +/-Inf
632 auto converted
{ConvertToDecimal(significantDigits
, rounding
, flags
)};
633 return {converted
.str
, static_cast<int>(converted
.length
), 0};
636 if (x_
.IsNegative()) {
637 buffer_
[len
++] = '-';
638 } else if (flags
& decimal::AlwaysSign
) {
639 buffer_
[len
++] = '+';
641 auto fraction
{x_
.Fraction()};
643 buffer_
[len
++] = '0';
644 return {buffer_
, len
, 0};
646 // Ensure that the MSB is set.
647 int expo
{x_
.UnbiasedExponent() - 3};
648 while (!(fraction
>> (x_
.binaryPrecision
- 1))) {
652 // This is initially the right shift count needed to bring the
653 // most-significant hexadecimal digit's bits into the LSBs.
654 // x_.binaryPrecision is constant, so / can be used for readability.
655 int shift
{x_
.binaryPrecision
- 4};
656 typename
BinaryFloatingPoint::RawType one
{1};
657 auto remaining
{(one
<< x_
.binaryPrecision
) - one
};
658 for (int digits
{0}; digits
< significantDigits
; ++digits
) {
659 if ((flags
& decimal::Minimize
) && !(fraction
& remaining
)) {
664 hexDigit
= int(fraction
>> shift
) & 0xf;
665 } else if (shift
>= -3) {
666 hexDigit
= int(fraction
<< -shift
) & 0xf;
668 if (hexDigit
>= 10) {
669 buffer_
[len
++] = 'A' + hexDigit
- 10;
671 buffer_
[len
++] = '0' + hexDigit
;
676 return {buffer_
, len
, expo
};
681 bool RealOutputEditing
<KIND
>::EditEXOutput(const DataEdit
&edit
) {
682 addSpaceBeforeCharacter(io_
);
683 int editDigits
{edit
.digits
.value_or(0)}; // 'd' field
684 int significantDigits
{editDigits
+ 1};
686 if (edit
.modes
.editingFlags
& signPlus
) {
687 flags
|= decimal::AlwaysSign
;
689 int editWidth
{edit
.width
.value_or(0)}; // 'w' field
690 if ((editWidth
== 0 && !edit
.digits
) || editDigits
== 0) {
692 flags
|= decimal::Minimize
;
693 significantDigits
= 28; // enough for 128-bit F.P.
696 ConvertToHexadecimal(significantDigits
, edit
.modes
.round
, flags
)};
697 if (IsInfOrNaN(converted
.str
, converted
.length
)) {
698 return editWidth
> 0 && converted
.length
> editWidth
699 ? EmitRepeated(io_
, '*', editWidth
)
700 : (editWidth
<= converted
.length
||
701 EmitRepeated(io_
, ' ', editWidth
- converted
.length
)) &&
702 EmitAscii(io_
, converted
.str
, converted
.length
);
704 int signLength
{converted
.length
> 0 &&
705 (converted
.str
[0] == '-' || converted
.str
[0] == '+')
708 int convertedDigits
{converted
.length
- signLength
};
710 const char *exponent
{FormatExponent(converted
.exponent
, edit
, expoLength
)};
711 int trailingZeroes
{flags
& decimal::Minimize
713 : std::max(0, significantDigits
- convertedDigits
)};
714 int totalLength
{converted
.length
+ trailingZeroes
+ expoLength
+ 3 /*0X.*/};
715 int width
{editWidth
> 0 ? editWidth
: totalLength
};
716 return totalLength
> width
|| !exponent
717 ? EmitRepeated(io_
, '*', width
)
718 : EmitRepeated(io_
, ' ', width
- totalLength
) &&
719 EmitAscii(io_
, converted
.str
, signLength
) &&
720 EmitAscii(io_
, "0X", 2) &&
721 EmitAscii(io_
, converted
.str
+ signLength
, 1) &&
723 io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
724 EmitAscii(io_
, converted
.str
+ signLength
+ 1,
725 converted
.length
- (signLength
+ 1)) &&
726 EmitRepeated(io_
, '0', trailingZeroes
) &&
727 EmitAscii(io_
, exponent
, expoLength
);
730 template <int KIND
> bool RealOutputEditing
<KIND
>::Edit(const DataEdit
&edit
) {
731 switch (edit
.descriptor
) {
733 return EditEorDOutput(edit
);
735 if (edit
.variation
== 'X') {
736 return EditEXOutput(edit
);
738 return EditEorDOutput(edit
);
741 return EditFOutput(edit
);
743 return EditBOZOutput
<1>(io_
, edit
,
744 reinterpret_cast<const unsigned char *>(&x_
),
745 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
747 return EditBOZOutput
<3>(io_
, edit
,
748 reinterpret_cast<const unsigned char *>(&x_
),
749 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
751 return EditBOZOutput
<4>(io_
, edit
,
752 reinterpret_cast<const unsigned char *>(&x_
),
753 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
755 return Edit(EditForGOutput(edit
));
757 return EditLogicalOutput(io_
, edit
, *reinterpret_cast<const char *>(&x_
));
758 case 'A': // legacy extension
759 return EditCharacterOutput(
760 io_
, edit
, reinterpret_cast<char *>(&x_
), sizeof x_
);
762 if (edit
.IsListDirected()) {
763 return EditListDirectedOutput(edit
);
765 io_
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
766 "Data edit descriptor '%c' may not be used with a REAL data item",
773 bool ListDirectedLogicalOutput(IoStatementState
&io
,
774 ListDirectedStatementState
<Direction::Output
> &list
, bool truth
) {
775 return list
.EmitLeadingSpaceOrAdvance(io
) &&
776 EmitAscii(io
, truth
? "T" : "F", 1);
779 bool EditLogicalOutput(IoStatementState
&io
, const DataEdit
&edit
, bool truth
) {
780 switch (edit
.descriptor
) {
783 return EmitRepeated(io
, ' ', std::max(0, edit
.width
.value_or(1) - 1)) &&
784 EmitAscii(io
, truth
? "T" : "F", 1);
786 return EditBOZOutput
<1>(io
, edit
,
787 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
789 return EditBOZOutput
<3>(io
, edit
,
790 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
792 return EditBOZOutput
<4>(io
, edit
,
793 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
795 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
796 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
802 template <typename CHAR
>
803 bool ListDirectedCharacterOutput(IoStatementState
&io
,
804 ListDirectedStatementState
<Direction::Output
> &list
, const CHAR
*x
,
805 std::size_t length
) {
807 MutableModes
&modes
{io
.mutableModes()};
808 ConnectionState
&connection
{io
.GetConnectionState()};
810 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
);
811 // Value is delimited with ' or " marks, and interior
812 // instances of that character are doubled.
813 auto EmitOne
{[&](CHAR ch
) {
814 if (connection
.NeedAdvance(1)) {
815 ok
= ok
&& io
.AdvanceRecord();
817 ok
= ok
&& EmitEncoded(io
, &ch
, 1);
819 EmitOne(modes
.delim
);
820 for (std::size_t j
{0}; j
< length
; ++j
) {
821 // Doubled delimiters must be put on the same record
822 // in order to be acceptable as list-directed or NAMELIST
823 // input; however, this requirement is not always possible
824 // when the records have a fixed length, as is the case with
825 // internal output. The standard is silent on what should
826 // happen, and no two extant Fortran implementations do
827 // the same thing when tested with this case.
828 // This runtime splits the doubled delimiters across
829 // two records for lack of a better alternative.
830 if (x
[j
] == static_cast<CHAR
>(modes
.delim
)) {
835 EmitOne(modes
.delim
);
837 // Undelimited list-directed output
838 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
, length
> 0 ? 1 : 0, true);
840 std::size_t oneAtATime
{
841 connection
.useUTF8
<CHAR
>() || connection
.internalIoCharKind
> 1
844 while (ok
&& put
< length
) {
845 if (std::size_t chunk
{std::min
<std::size_t>(
846 std::min
<std::size_t>(length
- put
, oneAtATime
),
847 connection
.RemainingSpaceInRecord())}) {
848 ok
= EmitEncoded(io
, x
+ put
, chunk
);
851 ok
= io
.AdvanceRecord() && EmitAscii(io
, " ", 1);
854 list
.set_lastWasUndelimitedCharacter(true);
859 template <typename CHAR
>
860 bool EditCharacterOutput(IoStatementState
&io
, const DataEdit
&edit
,
861 const CHAR
*x
, std::size_t length
) {
862 int len
{static_cast<int>(length
)};
863 int width
{edit
.width
.value_or(len
)};
864 switch (edit
.descriptor
) {
873 return EditBOZOutput
<1>(io
, edit
,
874 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
876 return EditBOZOutput
<3>(io
, edit
,
877 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
879 return EditBOZOutput
<4>(io
, edit
,
880 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
882 return EditLogicalOutput(io
, edit
, *reinterpret_cast<const char *>(x
));
884 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
885 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
889 return EmitRepeated(io
, ' ', std::max(0, width
- len
)) &&
890 EmitEncoded(io
, x
, std::min(width
, len
));
893 template bool EditIntegerOutput
<1>(
894 IoStatementState
&, const DataEdit
&, std::int8_t);
895 template bool EditIntegerOutput
<2>(
896 IoStatementState
&, const DataEdit
&, std::int16_t);
897 template bool EditIntegerOutput
<4>(
898 IoStatementState
&, const DataEdit
&, std::int32_t);
899 template bool EditIntegerOutput
<8>(
900 IoStatementState
&, const DataEdit
&, std::int64_t);
901 template bool EditIntegerOutput
<16>(
902 IoStatementState
&, const DataEdit
&, common::int128_t
);
904 template class RealOutputEditing
<2>;
905 template class RealOutputEditing
<3>;
906 template class RealOutputEditing
<4>;
907 template class RealOutputEditing
<8>;
908 template class RealOutputEditing
<10>;
909 // TODO: double/double
910 template class RealOutputEditing
<16>;
912 template bool ListDirectedCharacterOutput(IoStatementState
&,
913 ListDirectedStatementState
<Direction::Output
> &, const char *,
915 template bool ListDirectedCharacterOutput(IoStatementState
&,
916 ListDirectedStatementState
<Direction::Output
> &, const char16_t
*,
918 template bool ListDirectedCharacterOutput(IoStatementState
&,
919 ListDirectedStatementState
<Direction::Output
> &, const char32_t
*,
922 template bool EditCharacterOutput(
923 IoStatementState
&, const DataEdit
&, const char *, std::size_t chars
);
924 template bool EditCharacterOutput(
925 IoStatementState
&, const DataEdit
&, const char16_t
*, std::size_t chars
);
926 template bool EditCharacterOutput(
927 IoStatementState
&, const DataEdit
&, const char32_t
*, std::size_t chars
);
929 } // namespace Fortran::runtime::io