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
, *reinterpret_cast<const char *>(&n
));
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
> static_cast<std::size_t>(editWidth
)
345 ? EmitRepeated(io_
, '*', editWidth
)
346 : EmitPrefix(edit
, converted
.length
, editWidth
) &&
347 EmitAscii(io_
, converted
.str
, converted
.length
) &&
351 converted
.decimalExponent
-= scale
;
354 // EN mode: we need an effective exponent field that is
355 // a multiple of three.
356 if (int modulus
{converted
.decimalExponent
% 3}; modulus
!= 0) {
357 if (significantDigits
> 1) {
362 // Rounded nines up to a 1.
364 converted
.decimalExponent
-= modulus
;
367 int adjust
{3 * (scale
/ 3)};
369 converted
.decimalExponent
+= adjust
;
370 } else if (scale
< 1) {
371 int adjust
{3 - 3 * (scale
/ 3)};
373 converted
.decimalExponent
-= adjust
;
375 significantDigits
= editDigits
+ scale
;
377 // Format the exponent (see table 13.1 for all the cases)
379 const char *exponent
{
380 FormatExponent(converted
.decimalExponent
, edit
, expoLength
)};
381 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
382 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
383 int zeroesBeforePoint
{std::max(0, scale
- convertedDigits
)};
384 int digitsBeforePoint
{std::max(0, scale
- zeroesBeforePoint
)};
385 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
386 int trailingZeroes
{flags
& decimal::Minimize
389 significantDigits
- (convertedDigits
+ zeroesBeforePoint
))};
390 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
391 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingZeroes
+
393 int width
{editWidth
> 0 ? editWidth
: totalLength
};
394 if (totalLength
> width
|| !exponent
) {
395 return EmitRepeated(io_
, '*', width
);
397 if (totalLength
< width
&& digitsBeforePoint
== 0 &&
398 zeroesBeforePoint
== 0) {
399 zeroesBeforePoint
= 1;
402 if (totalLength
< width
&& editWidth
== 0) {
405 return EmitPrefix(edit
, totalLength
, width
) &&
406 EmitAscii(io_
, converted
.str
, signLength
+ digitsBeforePoint
) &&
407 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
408 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
409 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
410 EmitAscii(io_
, converted
.str
+ signLength
+ digitsBeforePoint
,
412 EmitRepeated(io_
, '0', trailingZeroes
) &&
413 EmitAscii(io_
, exponent
, expoLength
) && EmitSuffix(edit
);
417 // 13.7.2.3.2 in F'2018
419 bool RealOutputEditing
<KIND
>::EditFOutput(const DataEdit
&edit
) {
420 addSpaceBeforeCharacter(io_
);
421 int fracDigits
{edit
.digits
.value_or(0)}; // 'd' field
422 const int editWidth
{edit
.width
.value_or(0)}; // 'w' field
423 enum decimal::FortranRounding rounding
{edit
.modes
.round
};
425 if (edit
.modes
.editingFlags
& signPlus
) {
426 flags
|= decimal::AlwaysSign
;
428 if (editWidth
== 0) { // "the processor selects the field width"
429 if (!edit
.digits
.has_value()) { // F0
430 flags
|= decimal::Minimize
;
431 fracDigits
= sizeof buffer_
- 2; // sign & NUL
434 // Multiple conversions may be needed to get the right number of
435 // effective rounded fractional digits.
437 bool canIncrease
{true};
439 decimal::ConversionToDecimalResult converted
{
440 ConvertToDecimal(extraDigits
+ fracDigits
, rounding
, flags
)};
441 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
442 return editWidth
> 0 &&
443 converted
.length
> static_cast<std::size_t>(editWidth
)
444 ? EmitRepeated(io_
, '*', editWidth
)
445 : EmitPrefix(edit
, converted
.length
, editWidth
) &&
446 EmitAscii(io_
, converted
.str
, converted
.length
) &&
449 int expo
{converted
.decimalExponent
+ edit
.modes
.scale
/*kP*/};
450 int signLength
{*converted
.str
== '-' || *converted
.str
== '+' ? 1 : 0};
451 int convertedDigits
{static_cast<int>(converted
.length
) - signLength
};
452 if (IsZero()) { // don't treat converted "0" as significant digit
457 if (expo
> extraDigits
&& extraDigits
>= 0 && canIncrease
) {
459 if (!edit
.digits
.has_value()) { // F0
460 fracDigits
= sizeof buffer_
- extraDigits
- 2; // sign & NUL
462 canIncrease
= false; // only once
464 } else if (expo
== -fracDigits
&& convertedDigits
> 0) {
465 if ((rounding
== decimal::FortranRounding::RoundUp
&&
466 *converted
.str
!= '-') ||
467 (rounding
== decimal::FortranRounding::RoundDown
&&
468 *converted
.str
== '-') ||
469 (rounding
== decimal::FortranRounding::RoundToZero
&&
470 rounding
!= edit
.modes
.round
&& // it changed below
471 converted
.str
[signLength
] >= '5')) {
472 // Round up/down to a scaled 1
476 } else if (rounding
!= decimal::FortranRounding::RoundToZero
) {
477 // Convert again with truncation so first digit can be checked
478 // on the next iteration by the code above
479 rounding
= decimal::FortranRounding::RoundToZero
;
482 // Value rounds down to zero
486 } else if (expo
< extraDigits
&& extraDigits
> -fracDigits
) {
487 extraDigits
= std::max(expo
, -fracDigits
);
490 int digitsBeforePoint
{std::max(0, std::min(expo
, convertedDigits
))};
491 int zeroesBeforePoint
{std::max(0, expo
- digitsBeforePoint
)};
492 int zeroesAfterPoint
{std::min(fracDigits
, std::max(0, -expo
))};
493 int digitsAfterPoint
{convertedDigits
- digitsBeforePoint
};
494 int trailingZeroes
{flags
& decimal::Minimize
498 (zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
))};
499 if (digitsBeforePoint
+ zeroesBeforePoint
+ zeroesAfterPoint
+
500 digitsAfterPoint
+ trailingOnes
+ trailingZeroes
==
502 zeroesBeforePoint
= 1; // "." -> "0."
504 int totalLength
{signLength
+ digitsBeforePoint
+ zeroesBeforePoint
+
505 1 /*'.'*/ + zeroesAfterPoint
+ digitsAfterPoint
+ trailingOnes
+
507 int width
{editWidth
> 0 ? editWidth
: totalLength
};
508 if (totalLength
> width
) {
509 return EmitRepeated(io_
, '*', width
);
511 if (totalLength
< width
&& digitsBeforePoint
+ zeroesBeforePoint
== 0) {
512 zeroesBeforePoint
= 1;
515 return EmitPrefix(edit
, totalLength
, width
) &&
516 EmitAscii(io_
, converted
.str
, signLength
+ digitsBeforePoint
) &&
517 EmitRepeated(io_
, '0', zeroesBeforePoint
) &&
518 EmitAscii(io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
519 EmitRepeated(io_
, '0', zeroesAfterPoint
) &&
520 EmitAscii(io_
, converted
.str
+ signLength
+ digitsBeforePoint
,
522 EmitRepeated(io_
, '1', trailingOnes
) &&
523 EmitRepeated(io_
, '0', trailingZeroes
) &&
524 EmitRepeated(io_
, ' ', trailingBlanks_
) && EmitSuffix(edit
);
528 // 13.7.5.2.3 in F'2018
530 DataEdit RealOutputEditing
<KIND
>::EditForGOutput(DataEdit edit
) {
531 edit
.descriptor
= 'E';
532 int editWidth
{edit
.width
.value_or(0)};
533 int significantDigits
{
534 edit
.digits
.value_or(BinaryFloatingPoint::decimalPrecision
)}; // 'd'
535 if (editWidth
> 0 && significantDigits
== 0) {
536 return edit
; // Gw.0Ee -> Ew.0Ee for w > 0
539 if (edit
.modes
.editingFlags
& signPlus
) {
540 flags
|= decimal::AlwaysSign
;
542 decimal::ConversionToDecimalResult converted
{
543 ConvertToDecimal(significantDigits
, edit
.modes
.round
, flags
)};
544 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
545 return edit
; // Inf/Nan -> Ew.d (same as Fw.d)
547 int expo
{IsZero() ? 1 : converted
.decimalExponent
}; // 's'
548 if (expo
< 0 || expo
> significantDigits
) {
549 if (editWidth
== 0 && !edit
.expoDigits
) { // G0.d -> G0.dE0
552 return edit
; // Ew.dEe
554 edit
.descriptor
= 'F';
555 edit
.modes
.scale
= 0; // kP is ignored for G when no exponent field
558 int expoDigits
{edit
.expoDigits
.value_or(0)};
559 trailingBlanks_
= expoDigits
> 0 ? expoDigits
+ 2 : 4; // 'n'
560 *edit
.width
= std::max(0, editWidth
- trailingBlanks_
);
562 if (edit
.digits
.has_value()) {
563 *edit
.digits
= std::max(0, *edit
.digits
- expo
);
570 bool RealOutputEditing
<KIND
>::EditListDirectedOutput(const DataEdit
&edit
) {
571 decimal::ConversionToDecimalResult converted
{
572 ConvertToDecimal(1, edit
.modes
.round
)};
573 if (IsInfOrNaN(converted
.str
, static_cast<int>(converted
.length
))) {
574 return EditEorDOutput(edit
);
576 int expo
{converted
.decimalExponent
};
577 // The decimal precision of 16-bit floating-point types is very low,
578 // so use a reasonable cap of 6 to allow more values to be emitted
579 // with Fw.d editing.
580 static constexpr int maxExpo
{
581 std::max(6, BinaryFloatingPoint::decimalPrecision
)};
582 if (expo
< 0 || expo
> maxExpo
) {
584 copy
.modes
.scale
= 1; // 1P
585 return EditEorDOutput(copy
);
587 return EditFOutput(edit
);
590 // 13.7.2.3.6 in F'2023
591 // The specification for hexadecimal output, unfortunately for implementors,
592 // leaves as "implementation dependent" the choice of how to emit values
593 // with multiple hexadecimal output possibilities that are numerically
594 // equivalent. The one working implementation of EX output that I can find
595 // apparently chooses to frame the nybbles from most to least significant,
596 // rather than trying to minimize the magnitude of the binary exponent.
597 // E.g., 2. is edited into 0X8.0P-2 rather than 0X2.0P0. This implementation
598 // follows that precedent so as to avoid a gratuitous incompatibility.
600 auto RealOutputEditing
<KIND
>::ConvertToHexadecimal(
601 int significantDigits
, enum decimal::FortranRounding rounding
, int flags
)
602 -> ConvertToHexadecimalResult
{
603 if (x_
.IsNaN() || x_
.IsInfinite()) {
604 auto converted
{ConvertToDecimal(significantDigits
, rounding
, flags
)};
605 return {converted
.str
, static_cast<int>(converted
.length
), 0};
607 x_
.RoundToBits(4 * significantDigits
, rounding
);
608 if (x_
.IsInfinite()) { // rounded away to +/-Inf
609 auto converted
{ConvertToDecimal(significantDigits
, rounding
, flags
)};
610 return {converted
.str
, static_cast<int>(converted
.length
), 0};
613 if (x_
.IsNegative()) {
614 buffer_
[len
++] = '-';
615 } else if (flags
& decimal::AlwaysSign
) {
616 buffer_
[len
++] = '+';
618 auto fraction
{x_
.Fraction()};
620 buffer_
[len
++] = '0';
621 return {buffer_
, len
, 0};
623 // Ensure that the MSB is set.
624 int expo
{x_
.UnbiasedExponent() - 3};
625 while (!(fraction
>> (x_
.binaryPrecision
- 1))) {
629 // This is initially the right shift count needed to bring the
630 // most-significant hexadecimal digit's bits into the LSBs.
631 // x_.binaryPrecision is constant, so / can be used for readability.
632 int shift
{x_
.binaryPrecision
- 4};
633 typename
BinaryFloatingPoint::RawType one
{1};
634 auto remaining
{(one
<< shift
) - one
};
635 for (int digits
{0}; digits
< significantDigits
; ++digits
) {
636 if ((flags
& decimal::Minimize
) && !(fraction
& remaining
)) {
641 hexDigit
= int(fraction
>> shift
) & 0xf;
642 } else if (shift
>= -3) {
643 hexDigit
= int(fraction
<< -shift
) & 0xf;
645 if (hexDigit
>= 10) {
646 buffer_
[len
++] = 'A' + hexDigit
- 10;
648 buffer_
[len
++] = '0' + hexDigit
;
653 return {buffer_
, len
, expo
};
658 bool RealOutputEditing
<KIND
>::EditEXOutput(const DataEdit
&edit
) {
659 addSpaceBeforeCharacter(io_
);
660 int editDigits
{edit
.digits
.value_or(0)}; // 'd' field
661 int significantDigits
{editDigits
+ 1};
663 if (edit
.modes
.editingFlags
& signPlus
) {
664 flags
|= decimal::AlwaysSign
;
666 int editWidth
{edit
.width
.value_or(0)}; // 'w' field
667 if (editWidth
== 0 && !edit
.digits
) { // EX0 (no .d)
668 flags
|= decimal::Minimize
;
669 significantDigits
= 28; // enough for 128-bit F.P.
672 ConvertToHexadecimal(significantDigits
, edit
.modes
.round
, flags
)};
673 if (IsInfOrNaN(converted
.str
, converted
.length
)) {
674 return editWidth
> 0 && converted
.length
> editWidth
675 ? EmitRepeated(io_
, '*', editWidth
)
676 : (editWidth
<= converted
.length
||
677 EmitRepeated(io_
, ' ', editWidth
- converted
.length
)) &&
678 EmitAscii(io_
, converted
.str
, converted
.length
);
680 int signLength
{converted
.length
> 0 &&
681 (converted
.str
[0] == '-' || converted
.str
[0] == '+')
684 int convertedDigits
{converted
.length
- signLength
};
686 const char *exponent
{FormatExponent(converted
.exponent
, edit
, expoLength
)};
687 int trailingZeroes
{flags
& decimal::Minimize
689 : std::max(0, significantDigits
- convertedDigits
)};
690 int totalLength
{converted
.length
+ trailingZeroes
+ expoLength
+ 3 /*0X.*/};
691 int width
{editWidth
> 0 ? editWidth
: totalLength
};
692 return totalLength
> width
|| !exponent
693 ? EmitRepeated(io_
, '*', width
)
694 : EmitRepeated(io_
, ' ', width
- totalLength
) &&
695 EmitAscii(io_
, converted
.str
, signLength
) &&
696 EmitAscii(io_
, "0X", 2) &&
697 EmitAscii(io_
, converted
.str
+ signLength
, 1) &&
699 io_
, edit
.modes
.editingFlags
& decimalComma
? "," : ".", 1) &&
700 EmitAscii(io_
, converted
.str
+ signLength
+ 1,
701 converted
.length
- (signLength
+ 1)) &&
702 EmitRepeated(io_
, '0', trailingZeroes
) &&
703 EmitAscii(io_
, exponent
, expoLength
);
706 template <int KIND
> bool RealOutputEditing
<KIND
>::Edit(const DataEdit
&edit
) {
707 switch (edit
.descriptor
) {
709 return EditEorDOutput(edit
);
711 if (edit
.variation
== 'X') {
712 return EditEXOutput(edit
);
714 return EditEorDOutput(edit
);
717 return EditFOutput(edit
);
719 return EditBOZOutput
<1>(io_
, edit
,
720 reinterpret_cast<const unsigned char *>(&x_
),
721 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
723 return EditBOZOutput
<3>(io_
, edit
,
724 reinterpret_cast<const unsigned char *>(&x_
),
725 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
727 return EditBOZOutput
<4>(io_
, edit
,
728 reinterpret_cast<const unsigned char *>(&x_
),
729 common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND
)) >> 3);
731 return Edit(EditForGOutput(edit
));
733 return EditLogicalOutput(io_
, edit
, *reinterpret_cast<const char *>(&x_
));
734 case 'A': // legacy extension
735 return EditCharacterOutput(
736 io_
, edit
, reinterpret_cast<char *>(&x_
), sizeof x_
);
738 if (edit
.IsListDirected()) {
739 return EditListDirectedOutput(edit
);
741 io_
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
742 "Data edit descriptor '%c' may not be used with a REAL data item",
749 bool ListDirectedLogicalOutput(IoStatementState
&io
,
750 ListDirectedStatementState
<Direction::Output
> &list
, bool truth
) {
751 return list
.EmitLeadingSpaceOrAdvance(io
) &&
752 EmitAscii(io
, truth
? "T" : "F", 1);
755 bool EditLogicalOutput(IoStatementState
&io
, const DataEdit
&edit
, bool truth
) {
756 switch (edit
.descriptor
) {
759 return EmitRepeated(io
, ' ', std::max(0, edit
.width
.value_or(1) - 1)) &&
760 EmitAscii(io
, truth
? "T" : "F", 1);
762 return EditBOZOutput
<1>(io
, edit
,
763 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
765 return EditBOZOutput
<3>(io
, edit
,
766 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
768 return EditBOZOutput
<4>(io
, edit
,
769 reinterpret_cast<const unsigned char *>(&truth
), sizeof truth
);
771 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
772 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
778 template <typename CHAR
>
779 bool ListDirectedCharacterOutput(IoStatementState
&io
,
780 ListDirectedStatementState
<Direction::Output
> &list
, const CHAR
*x
,
781 std::size_t length
) {
783 MutableModes
&modes
{io
.mutableModes()};
784 ConnectionState
&connection
{io
.GetConnectionState()};
786 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
);
787 // Value is delimited with ' or " marks, and interior
788 // instances of that character are doubled.
789 auto EmitOne
{[&](CHAR ch
) {
790 if (connection
.NeedAdvance(1)) {
791 ok
= ok
&& io
.AdvanceRecord();
793 ok
= ok
&& EmitEncoded(io
, &ch
, 1);
795 EmitOne(modes
.delim
);
796 for (std::size_t j
{0}; j
< length
; ++j
) {
797 // Doubled delimiters must be put on the same record
798 // in order to be acceptable as list-directed or NAMELIST
799 // input; however, this requirement is not always possible
800 // when the records have a fixed length, as is the case with
801 // internal output. The standard is silent on what should
802 // happen, and no two extant Fortran implementations do
803 // the same thing when tested with this case.
804 // This runtime splits the doubled delimiters across
805 // two records for lack of a better alternative.
806 if (x
[j
] == static_cast<CHAR
>(modes
.delim
)) {
811 EmitOne(modes
.delim
);
813 // Undelimited list-directed output
814 ok
= ok
&& list
.EmitLeadingSpaceOrAdvance(io
, length
> 0 ? 1 : 0, true);
816 std::size_t oneAtATime
{
817 connection
.useUTF8
<CHAR
>() || connection
.internalIoCharKind
> 1
820 while (ok
&& put
< length
) {
821 if (std::size_t chunk
{std::min
<std::size_t>(
822 std::min
<std::size_t>(length
- put
, oneAtATime
),
823 connection
.RemainingSpaceInRecord())}) {
824 ok
= EmitEncoded(io
, x
+ put
, chunk
);
827 ok
= io
.AdvanceRecord() && EmitAscii(io
, " ", 1);
830 list
.set_lastWasUndelimitedCharacter(true);
835 template <typename CHAR
>
836 bool EditCharacterOutput(IoStatementState
&io
, const DataEdit
&edit
,
837 const CHAR
*x
, std::size_t length
) {
838 int len
{static_cast<int>(length
)};
839 int width
{edit
.width
.value_or(len
)};
840 switch (edit
.descriptor
) {
849 return EditBOZOutput
<1>(io
, edit
,
850 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
852 return EditBOZOutput
<3>(io
, edit
,
853 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
855 return EditBOZOutput
<4>(io
, edit
,
856 reinterpret_cast<const unsigned char *>(x
), sizeof(CHAR
) * length
);
858 return EditLogicalOutput(io
, edit
, *reinterpret_cast<const char *>(x
));
860 io
.GetIoErrorHandler().SignalError(IostatErrorInFormat
,
861 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
865 return EmitRepeated(io
, ' ', std::max(0, width
- len
)) &&
866 EmitEncoded(io
, x
, std::min(width
, len
));
869 template bool EditIntegerOutput
<1>(
870 IoStatementState
&, const DataEdit
&, std::int8_t);
871 template bool EditIntegerOutput
<2>(
872 IoStatementState
&, const DataEdit
&, std::int16_t);
873 template bool EditIntegerOutput
<4>(
874 IoStatementState
&, const DataEdit
&, std::int32_t);
875 template bool EditIntegerOutput
<8>(
876 IoStatementState
&, const DataEdit
&, std::int64_t);
877 template bool EditIntegerOutput
<16>(
878 IoStatementState
&, const DataEdit
&, common::int128_t
);
880 template class RealOutputEditing
<2>;
881 template class RealOutputEditing
<3>;
882 template class RealOutputEditing
<4>;
883 template class RealOutputEditing
<8>;
884 template class RealOutputEditing
<10>;
885 // TODO: double/double
886 template class RealOutputEditing
<16>;
888 template bool ListDirectedCharacterOutput(IoStatementState
&,
889 ListDirectedStatementState
<Direction::Output
> &, const char *,
891 template bool ListDirectedCharacterOutput(IoStatementState
&,
892 ListDirectedStatementState
<Direction::Output
> &, const char16_t
*,
894 template bool ListDirectedCharacterOutput(IoStatementState
&,
895 ListDirectedStatementState
<Direction::Output
> &, const char32_t
*,
898 template bool EditCharacterOutput(
899 IoStatementState
&, const DataEdit
&, const char *, std::size_t chars
);
900 template bool EditCharacterOutput(
901 IoStatementState
&, const DataEdit
&, const char16_t
*, std::size_t chars
);
902 template bool EditCharacterOutput(
903 IoStatementState
&, const DataEdit
&, const char32_t
*, std::size_t chars
);
905 } // namespace Fortran::runtime::io