Skip to content

Commit dce1f28

Browse files
committed
Merge remote-tracking branch 'origin/main' into rebranch
2 parents 2af2752 + a32dacb commit dce1f28

File tree

4 files changed

+2238
-2058
lines changed

4 files changed

+2238
-2058
lines changed

include/swift/Runtime/SwiftDtoa.h

Lines changed: 233 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,152 +2,283 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018, 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===---------------------------------------------------------------------===//
12+
//
13+
/// About SwiftDtoa
14+
/// ===============
15+
///
16+
/// SwiftDtoa is the C implementation that supports the `.description`
17+
/// and `.debugDescription` properties for the standard Swift
18+
/// floating-point types. These functions produce the "optimal form"
19+
/// for the binary floating point value. The optimal form is a
20+
/// decimal representation that satisfies the following properties:
21+
///
22+
/// 1. Accurate. Parsing the value back to a binary floating-point
23+
/// value of the same precision will exactly yield the original
24+
/// value. For example, `Double(d.description) == d` for all `Double`
25+
/// values `d` (except for NaN values, of course).
26+
///
27+
/// 2. Short. Of all accurate results, the returned value will
28+
/// contain the minimum number of significant digits. Note that
29+
/// this is not quite the same as C++ `to_chars` which promises the
30+
/// minimal number of characters.
31+
///
32+
/// 3. Close. Of all accurate, short results, the value printed will
33+
/// be the one that is closest to the exact binary floating-point
34+
/// value.
35+
///
36+
/// The optimal form is the ideal textual form for use in JSON and
37+
/// similar interchange formats because it is accurate, compact, and
38+
/// can be generated very quickly. It is also ideal for logging and
39+
/// debugging use; the accuracy guarantees that the result can be
40+
/// cut-and-pasted to obtain the exact original value, and the
41+
/// shortness property eliminates unnecessary digits that can be
42+
/// confusing to readers.
43+
///
44+
/// Algorithms that produce such output have been known since at least
45+
/// 1990, when Steele and White published their Dragon4 algorithm.
46+
/// However, the earliest algorithms required high-precision
47+
/// arithmetic which limited their use. Starting in 2010 with the
48+
/// publication of Grisu3, there has been a surge of interest and
49+
/// there are now a number of algorithms that can produce optimal
50+
/// forms very quickly. This particular implementation is loosely
51+
/// based on Grisu2 but incorporates concepts from Errol and Ryu that
52+
/// make it significantly faster and ensure accuracy in all cases.
53+
///
54+
/// About SwiftDtoa v1
55+
/// ------------------
56+
///
57+
/// The first version of SwiftDtoa was committed to the Swift runtime
58+
/// in 2018. It supported Swift's Float, Double, and Float80 formats.
59+
///
60+
/// About SwiftDtoa v1a
61+
/// -------------------
62+
///
63+
/// Version 1a of SwiftDtoa added support for Float16.
64+
///
65+
/// About SwiftDtoa v2
66+
/// ------------------
67+
///
68+
/// Version 2 of SwiftDtoa is a major overhaul with a number of
69+
/// algorithmic improvements to make it faster (especially for Float16
70+
/// and Float80), smaller, and more portable (the code only requires
71+
/// C99 and makes no use of C or C++ floating-point facilities). It
72+
/// also includes experimental support for IEEE 754 quad-precision
73+
/// binary128 format, which is not currently supported by Swift.
74+
//
75+
//===---------------------------------------------------------------------===//
1276

1377
#ifndef SWIFT_DTOA_H
1478
#define SWIFT_DTOA_H
1579

80+
#include <float.h>
1681
#include <stdbool.h>
1782
#include <stdint.h>
1883
#include <stdlib.h>
1984

20-
// This implementation strongly assumes that `float` is
21-
// IEEE 754 single-precision binary32 format and that
22-
// `double` is IEEE 754 double-precision binary64 format.
23-
24-
// Essentially all modern platforms use IEEE 754 floating point
25-
// types now, so enable these by default:
26-
#define SWIFT_DTOA_FLOAT16_SUPPORT 1
27-
#define SWIFT_DTOA_FLOAT_SUPPORT 1
28-
#define SWIFT_DTOA_DOUBLE_SUPPORT 1
29-
30-
// This implementation assumes `long double` is Intel 80-bit extended format.
31-
#if defined(_WIN32)
32-
// Windows has `long double` == `double` on all platforms, so disable this.
33-
#undef SWIFT_DTOA_FLOAT80_SUPPORT
34-
#elif defined(__ANDROID__)
35-
// At least for now Float80 is disabled. See: https://github.com/apple/swift/pull/25502
36-
#elif defined(__APPLE__) || defined(__linux__) || defined(__OpenBSD__)
37-
// macOS and Linux support Float80 on X86 hardware but not on ARM
38-
#if defined(__x86_64__) || defined(__i386)
85+
//
86+
// IEEE 754 Binary16 support (also known as "half-precision")
87+
//
88+
89+
// Enable this by default.
90+
// Force disable: -DSWIFT_DTOA_BINARY16_SUPPORT=0
91+
#ifndef SWIFT_DTOA_BINARY16_SUPPORT
92+
#define SWIFT_DTOA_BINARY16_SUPPORT 1
93+
#endif
94+
95+
//
96+
// IEEE 754 Binary32 support (also known as "single-precision")
97+
//
98+
99+
// Does "float" on this system use binary32 format?
100+
// (Almost all modern systems do this.)
101+
#if (FLT_RADIX == 2) && (FLT_MANT_DIG == 24) && (FLT_MIN_EXP == -125) && (FLT_MAX_EXP == 128)
102+
#define FLOAT_IS_BINARY32 1
103+
#else
104+
#undef FLOAT_IS_BINARY32
105+
#endif
106+
107+
// We can format binary32 values even if the local C environment
108+
// does not support it. But `float` == binary32 almost everywhere,
109+
// so we enable it by default.
110+
// Force disable: -DSWIFT_DTOA_BINARY32_SUPPORT=0
111+
#ifndef SWIFT_DTOA_BINARY32_SUPPORT
112+
#define SWIFT_DTOA_BINARY32_SUPPORT 1
113+
#endif
114+
115+
//
116+
// IEEE 754 Binary64 support (also known as "double-precision")
117+
//
118+
119+
// Does "double" on this system use binary64 format?
120+
// (Almost all modern systems do this.)
121+
#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MIN_EXP == -1021) && (DBL_MAX_EXP == 1024)
122+
#define DOUBLE_IS_BINARY64 1
123+
#else
124+
#undef DOUBLE_IS_BINARY64
125+
#endif
126+
127+
// Does "long double" on this system use binary64 format?
128+
// (Windows, for example.)
129+
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 53) && (LDBL_MIN_EXP == -1021) && (LDBL_MAX_EXP == 1024)
130+
#define LONG_DOUBLE_IS_BINARY64 1
131+
#else
132+
#undef LONG_DOUBLE_IS_BINARY64
133+
#endif
134+
135+
// We can format binary64 values even if the local C environment
136+
// does not support it. But `double` == binary64 almost everywhere,
137+
// so we enable it by default.
138+
// Force disable: -DSWIFT_DTOA_BINARY64_SUPPORT=0
139+
#ifndef SWIFT_DTOA_BINARY64_SUPPORT
140+
#define SWIFT_DTOA_BINARY64_SUPPORT 1
141+
#endif
142+
143+
//
144+
// Intel x87 Float80 support
145+
//
146+
147+
// Is "long double" on this system the same as Float80?
148+
// (macOS, Linux, and FreeBSD when running on x86 or x86_64 processors.)
149+
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 64) && (LDBL_MIN_EXP == -16381) && (LDBL_MAX_EXP == 16384)
150+
#define LONG_DOUBLE_IS_FLOAT80 1
151+
#else
152+
#undef LONG_DOUBLE_IS_FLOAT80
153+
#endif
154+
155+
// We can format float80 values even if the local C environment
156+
// does not support it. However, by default, we only enable it for
157+
// environments where float80 == long double.
158+
// Force enable: -DSWIFT_DTOA_FLOAT80_SUPPORT=1
159+
// Force disable: -DSWIFT_DTOA_FLOAT80_SUPPORT=0
160+
#ifndef SWIFT_DTOA_FLOAT80_SUPPORT
161+
#if LONG_DOUBLE_IS_FLOAT80
39162
#define SWIFT_DTOA_FLOAT80_SUPPORT 1
40163
#endif
41164
#endif
42165

166+
//
167+
// IEEE 754 Binary128 support
168+
//
169+
170+
// Is "long double" on this system the same as Binary128?
171+
// (Android on LP64 hardware.)
172+
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 113) && (LDBL_MIN_EXP == -16381) && (LDBL_MAX_EXP == 16384)
173+
#define LONG_DOUBLE_IS_BINARY128 1
174+
#else
175+
#undef LONG_DOUBLE_IS_BINARY128
176+
#endif
177+
178+
// We can format binary128 values even if the local C environment
179+
// does not support it. However, by default, we only enable it for
180+
// environments where binary128 == long double.
181+
// Force enable: -DSWIFT_DTOA_BINARY128_SUPPORT=1
182+
// Force disable: -DSWIFT_DTOA_BINARY128_SUPPORT=0
183+
#ifndef SWIFT_DTOA_BINARY128_SUPPORT
184+
#if LONG_DOUBLE_IS_BINARY128
185+
#define SWIFT_DTOA_BINARY128_SUPPORT 1
186+
#endif
187+
#endif
188+
43189
#ifdef __cplusplus
44190
extern "C" {
45191
#endif
46192

47-
#if SWIFT_DTOA_DOUBLE_SUPPORT
48-
// Compute the optimal decimal digits and exponent for a double.
193+
// Format a floating point value as an ASCII string
49194
//
50195
// Input:
51-
// * `d` is the number to be decomposed
52-
// * `digits` is an array of `digits_length`
53-
// * `decimalExponent` is a pointer to an `int`
196+
// * `d` is the number to be formatted
197+
// * `dest` is a buffer of length `length`
54198
//
55199
// Ouput:
56-
// * `digits` will receive the decimal digits
57-
// * `decimalExponent` will receive the decimal exponent
58-
// * function returns the number of digits generated
59-
// * the sign of the input number is ignored
200+
// * Return value is the length of the string placed into `dest`
201+
// or zero if the buffer is too small.
202+
// * For infinity, it copies "inf" or "-inf".
203+
// * For NaN, it outputs a Swift-style detailed dump, including
204+
// sign, signaling/quiet, and payload (if any). Typical output:
205+
// "nan", "-nan", "-snan(0x1234)".
206+
// * For zero, it outputs "0.0" or "-0.0" depending on the sign.
207+
// * The destination buffer is always null-terminated (even on error)
208+
// unless the length is zero.
209+
//
210+
// Note: If you want to customize the output for Infinity, zero, or
211+
// Nan, you can easily write a wrapper function that uses `fpclassify`
212+
// to identify those cases and only calls through to these functions
213+
// for normal and subnormal values.
60214
//
61215
// Guarantees:
62216
//
63-
// * Accurate. If you parse the result back to a double via an accurate
64-
// algorithm (such as Clinger's algorithm), the resulting double will
65-
// be exactly equal to the original value. On most systems, this
66-
// implies that using `strtod` to parse the output of
67-
// `swift_format_double` will yield exactly the original value.
217+
// * Accurate. If you parse the result back to the same floating-point
218+
// format via an accurate algorithm (such as Clinger's algorithm),
219+
// the resulting value will be _exactly_ equal to the original value.
220+
// On most systems, this implies that using `strtod` to parse the
221+
// output of `swift_dtoa_optimal_double` will yield exactly the
222+
// original value.
68223
//
69224
// * Short. No other accurate result will have fewer digits.
70225
//
71226
// * Close. If there are multiple possible decimal forms that are
72227
// both accurate and short, the form computed here will be
73228
// closest to the original binary value.
74229
//
75-
// Notes:
76-
//
77-
// If the input value is infinity or NaN, or `digits_length < 17`, the
78-
// function returns zero and generates no ouput.
79-
//
80-
// If the input value is zero, it will return `decimalExponent = 0` and
81-
// a single digit of value zero.
82-
//
83-
int swift_decompose_double(double d,
84-
int8_t *digits, size_t digits_length, int *decimalExponent);
230+
// Naming: The `_p` forms take a `const void *` pointing to the value
231+
// in memory. These forms do not require any support from the local C
232+
// environment. In particular, they should work correctly even on
233+
// systems with no floating-point support. Forms ending in a C
234+
// floating-point type (e.g., "_float", "_double") are identical but
235+
// take the corresponding argument type. These forms obviously
236+
// require the C environment to support passing floating-point types as
237+
// function arguments.
85238

86-
// Format a double as an ASCII string.
87-
//
88-
// For infinity, it outputs "inf" or "-inf".
89-
//
90-
// For NaN, it outputs a Swift-style detailed dump, including
91-
// sign, signaling/quiet, and payload (if any). Typical output:
92-
// "nan", "-nan", "-snan(0x1234)".
93-
//
94-
// For zero, it outputs "0.0" or "-0.0" depending on the sign.
95-
//
96-
// For other values, it uses `swift_decompose_double` to compute the
97-
// digits, then uses either `swift_format_decimal` or
98-
// `swift_format_exponential` to produce an ASCII string depending on
99-
// the magnitude of the value.
100-
//
101-
// In all cases, it returns the number of ASCII characters actually
102-
// written, or zero if the buffer was too small.
103-
size_t swift_format_double(double, char *dest, size_t length);
239+
#if SWIFT_DTOA_BINARY16_SUPPORT
240+
size_t swift_dtoa_optimal_binary16_p(const void *, char *dest, size_t length);
104241
#endif
105242

106-
#if SWIFT_DTOA_FLOAT16_SUPPORT
107-
// See swift_decompose_double. `digits_length` must be at least 5.
108-
int swift_decompose_float16(const __fp16 *f,
109-
int8_t *digits, size_t digits_length, int *decimalExponent);
110-
// See swift_format_double.
111-
size_t swift_format_float16(const __fp16 *, char *dest, size_t length);
243+
#if SWIFT_DTOA_BINARY32_SUPPORT
244+
size_t swift_dtoa_optimal_binary32_p(const void *, char *dest, size_t length);
245+
#if FLOAT_IS_BINARY32
246+
// If `float` happens to be binary32, define the convenience wrapper.
247+
size_t swift_dtoa_optimal_float(float, char *dest, size_t length);
248+
#endif
112249
#endif
113250

114-
#if SWIFT_DTOA_FLOAT_SUPPORT
115-
// See swift_decompose_double. `digits_length` must be at least 9.
116-
int swift_decompose_float(float f,
117-
int8_t *digits, size_t digits_length, int *decimalExponent);
118-
// See swift_format_double.
119-
size_t swift_format_float(float, char *dest, size_t length);
251+
#if SWIFT_DTOA_BINARY64_SUPPORT
252+
size_t swift_dtoa_optimal_binary64_p(const void *, char *dest, size_t length);
253+
#if DOUBLE_IS_BINARY64
254+
// If `double` happens to be binary64, define the convenience wrapper.
255+
size_t swift_dtoa_optimal_double(double, char *dest, size_t length);
256+
#endif
257+
#if LONG_DOUBLE_IS_BINARY64
258+
// If `long double` happens to be binary64, define the convenience wrapper.
259+
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
260+
#endif
120261
#endif
121262

122263
#if SWIFT_DTOA_FLOAT80_SUPPORT
123-
// See swift_decompose_double. `digits_length` must be at least 21.
124-
int swift_decompose_float80(long double f,
125-
int8_t *digits, size_t digits_length, int *decimalExponent);
126-
// See swift_format_double.
127-
size_t swift_format_float80(long double, char *dest, size_t length);
128-
#endif
129-
130-
// Generate an ASCII string from the raw exponent and digit information
131-
// as generated by `swift_decompose_double`. Returns the number of
132-
// bytes actually used. If `dest` was not big enough, these functions
133-
// return zero. The generated string is always terminated with a zero
134-
// byte unless `length` was zero.
135-
136-
// "Exponential" form uses common exponential format, e.g., "-1.234e+56"
137-
// The exponent always has a sign and at least two digits. The
138-
// generated string is never longer than `digits_count + 9` bytes,
139-
// including the trailing zero byte.
140-
size_t swift_format_exponential(char *dest, size_t length,
141-
bool negative, const int8_t *digits, int digits_count, int decimalExponent);
142-
143-
// "Decimal" form writes the value without using exponents. This
144-
// includes cases such as "0.000001234", "123.456", and "123456000.0".
145-
// Note that the result always has a decimal point with at least one
146-
// digit before and one digit after. The generated string is never
147-
// longer than `digits_count + abs(exponent) + 4` bytes, including the
148-
// trailing zero byte.
149-
size_t swift_format_decimal(char *dest, size_t length,
150-
bool negative, const int8_t *digits, int digits_count, int decimalExponent);
264+
// Universal entry point works on all platforms, regardless of
265+
// whether the local system has direct support for float80
266+
size_t swift_dtoa_optimal_float80_p(const void *, char *dest, size_t length);
267+
#if LONG_DOUBLE_IS_FLOAT80
268+
// If 'long double' happens to be float80, define a convenience wrapper.
269+
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
270+
#endif
271+
#endif
272+
273+
#if SWIFT_DTOA_BINARY128_SUPPORT
274+
// Universal entry point works on all platforms, regardless of
275+
// whether the local system has direct support for float80
276+
size_t swift_dtoa_optimal_binary128_p(const void *, char *dest, size_t length);
277+
#if LONG_DOUBLE_IS_BINARY128
278+
// If 'long double' happens to be binary128, define a convenience wrapper.
279+
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
280+
#endif
281+
#endif
151282

152283
#ifdef __cplusplus
153284
}

0 commit comments

Comments
 (0)