Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 233 additions & 102 deletions include/swift/Runtime/SwiftDtoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,152 +2,283 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Copyright (c) 2018, 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===---------------------------------------------------------------------===//
//
/// About SwiftDtoa
/// ===============
///
/// SwiftDtoa is the C implementation that supports the `.description`
/// and `.debugDescription` properties for the standard Swift
/// floating-point types. These functions produce the "optimal form"
/// for the binary floating point value. The optimal form is a
/// decimal representation that satisfies the following properties:
///
/// 1. Accurate. Parsing the value back to a binary floating-point
/// value of the same precision will exactly yield the original
/// value. For example, `Double(d.description) == d` for all `Double`
/// values `d` (except for NaN values, of course).
///
/// 2. Short. Of all accurate results, the returned value will
/// contain the minimum number of significant digits. Note that
/// this is not quite the same as C++ `to_chars` which promises the
/// minimal number of characters.
///
/// 3. Close. Of all accurate, short results, the value printed will
/// be the one that is closest to the exact binary floating-point
/// value.
///
/// The optimal form is the ideal textual form for use in JSON and
/// similar interchange formats because it is accurate, compact, and
/// can be generated very quickly. It is also ideal for logging and
/// debugging use; the accuracy guarantees that the result can be
/// cut-and-pasted to obtain the exact original value, and the
/// shortness property eliminates unnecessary digits that can be
/// confusing to readers.
///
/// Algorithms that produce such output have been known since at least
/// 1990, when Steele and White published their Dragon4 algorithm.
/// However, the earliest algorithms required high-precision
/// arithmetic which limited their use. Starting in 2010 with the
/// publication of Grisu3, there has been a surge of interest and
/// there are now a number of algorithms that can produce optimal
/// forms very quickly. This particular implementation is loosely
/// based on Grisu2 but incorporates concepts from Errol and Ryu that
/// make it significantly faster and ensure accuracy in all cases.
///
/// About SwiftDtoa v1
/// ------------------
///
/// The first version of SwiftDtoa was committed to the Swift runtime
/// in 2018. It supported Swift's Float, Double, and Float80 formats.
///
/// About SwiftDtoa v1a
/// -------------------
///
/// Version 1a of SwiftDtoa added support for Float16.
///
/// About SwiftDtoa v2
/// ------------------
///
/// Version 2 of SwiftDtoa is a major overhaul with a number of
/// algorithmic improvements to make it faster (especially for Float16
/// and Float80), smaller, and more portable (the code only requires
/// C99 and makes no use of C or C++ floating-point facilities). It
/// also includes experimental support for IEEE 754 quad-precision
/// binary128 format, which is not currently supported by Swift.
//
//===---------------------------------------------------------------------===//

#ifndef SWIFT_DTOA_H
#define SWIFT_DTOA_H

#include <float.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

// This implementation strongly assumes that `float` is
// IEEE 754 single-precision binary32 format and that
// `double` is IEEE 754 double-precision binary64 format.

// Essentially all modern platforms use IEEE 754 floating point
// types now, so enable these by default:
#define SWIFT_DTOA_FLOAT16_SUPPORT 1
#define SWIFT_DTOA_FLOAT_SUPPORT 1
#define SWIFT_DTOA_DOUBLE_SUPPORT 1

// This implementation assumes `long double` is Intel 80-bit extended format.
#if defined(_WIN32)
// Windows has `long double` == `double` on all platforms, so disable this.
#undef SWIFT_DTOA_FLOAT80_SUPPORT
#elif defined(__ANDROID__)
// At least for now Float80 is disabled. See: https://github.com/apple/swift/pull/25502
#elif defined(__APPLE__) || defined(__linux__) || defined(__OpenBSD__)
// macOS and Linux support Float80 on X86 hardware but not on ARM
#if defined(__x86_64__) || defined(__i386)
//
// IEEE 754 Binary16 support (also known as "half-precision")
//

// Enable this by default.
// Force disable: -DSWIFT_DTOA_BINARY16_SUPPORT=0
#ifndef SWIFT_DTOA_BINARY16_SUPPORT
#define SWIFT_DTOA_BINARY16_SUPPORT 1
#endif

//
// IEEE 754 Binary32 support (also known as "single-precision")
//

// Does "float" on this system use binary32 format?
// (Almost all modern systems do this.)
#if (FLT_RADIX == 2) && (FLT_MANT_DIG == 24) && (FLT_MIN_EXP == -125) && (FLT_MAX_EXP == 128)
#define FLOAT_IS_BINARY32 1
#else
#undef FLOAT_IS_BINARY32
#endif

// We can format binary32 values even if the local C environment
// does not support it. But `float` == binary32 almost everywhere,
// so we enable it by default.
// Force disable: -DSWIFT_DTOA_BINARY32_SUPPORT=0
#ifndef SWIFT_DTOA_BINARY32_SUPPORT
#define SWIFT_DTOA_BINARY32_SUPPORT 1
#endif

//
// IEEE 754 Binary64 support (also known as "double-precision")
//

// Does "double" on this system use binary64 format?
// (Almost all modern systems do this.)
#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MIN_EXP == -1021) && (DBL_MAX_EXP == 1024)
#define DOUBLE_IS_BINARY64 1
#else
#undef DOUBLE_IS_BINARY64
#endif

// Does "long double" on this system use binary64 format?
// (Windows, for example.)
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 53) && (LDBL_MIN_EXP == -1021) && (LDBL_MAX_EXP == 1024)
#define LONG_DOUBLE_IS_BINARY64 1
#else
#undef LONG_DOUBLE_IS_BINARY64
#endif

// We can format binary64 values even if the local C environment
// does not support it. But `double` == binary64 almost everywhere,
// so we enable it by default.
// Force disable: -DSWIFT_DTOA_BINARY64_SUPPORT=0
#ifndef SWIFT_DTOA_BINARY64_SUPPORT
#define SWIFT_DTOA_BINARY64_SUPPORT 1
#endif

//
// Intel x87 Float80 support
//

// Is "long double" on this system the same as Float80?
// (macOS, Linux, and FreeBSD when running on x86 or x86_64 processors.)
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 64) && (LDBL_MIN_EXP == -16381) && (LDBL_MAX_EXP == 16384)
#define LONG_DOUBLE_IS_FLOAT80 1
#else
#undef LONG_DOUBLE_IS_FLOAT80
#endif

// We can format float80 values even if the local C environment
// does not support it. However, by default, we only enable it for
// environments where float80 == long double.
// Force enable: -DSWIFT_DTOA_FLOAT80_SUPPORT=1
// Force disable: -DSWIFT_DTOA_FLOAT80_SUPPORT=0
#ifndef SWIFT_DTOA_FLOAT80_SUPPORT
#if LONG_DOUBLE_IS_FLOAT80
#define SWIFT_DTOA_FLOAT80_SUPPORT 1
#endif
#endif

//
// IEEE 754 Binary128 support
//

// Is "long double" on this system the same as Binary128?
// (Android on LP64 hardware.)
#if (FLT_RADIX == 2) && (LDBL_MANT_DIG == 113) && (LDBL_MIN_EXP == -16381) && (LDBL_MAX_EXP == 16384)
#define LONG_DOUBLE_IS_BINARY128 1
#else
#undef LONG_DOUBLE_IS_BINARY128
#endif

// We can format binary128 values even if the local C environment
// does not support it. However, by default, we only enable it for
// environments where binary128 == long double.
// Force enable: -DSWIFT_DTOA_BINARY128_SUPPORT=1
// Force disable: -DSWIFT_DTOA_BINARY128_SUPPORT=0
#ifndef SWIFT_DTOA_BINARY128_SUPPORT
#if LONG_DOUBLE_IS_BINARY128
#define SWIFT_DTOA_BINARY128_SUPPORT 1
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if SWIFT_DTOA_DOUBLE_SUPPORT
// Compute the optimal decimal digits and exponent for a double.
// Format a floating point value as an ASCII string
//
// Input:
// * `d` is the number to be decomposed
// * `digits` is an array of `digits_length`
// * `decimalExponent` is a pointer to an `int`
// * `d` is the number to be formatted
// * `dest` is a buffer of length `length`
//
// Ouput:
// * `digits` will receive the decimal digits
// * `decimalExponent` will receive the decimal exponent
// * function returns the number of digits generated
// * the sign of the input number is ignored
// * Return value is the length of the string placed into `dest`
// or zero if the buffer is too small.
// * For infinity, it copies "inf" or "-inf".
// * For NaN, it outputs a Swift-style detailed dump, including
// sign, signaling/quiet, and payload (if any). Typical output:
// "nan", "-nan", "-snan(0x1234)".
// * For zero, it outputs "0.0" or "-0.0" depending on the sign.
// * The destination buffer is always null-terminated (even on error)
// unless the length is zero.
//
// Note: If you want to customize the output for Infinity, zero, or
// Nan, you can easily write a wrapper function that uses `fpclassify`
// to identify those cases and only calls through to these functions
// for normal and subnormal values.
//
// Guarantees:
//
// * Accurate. If you parse the result back to a double via an accurate
// algorithm (such as Clinger's algorithm), the resulting double will
// be exactly equal to the original value. On most systems, this
// implies that using `strtod` to parse the output of
// `swift_format_double` will yield exactly the original value.
// * Accurate. If you parse the result back to the same floating-point
// format via an accurate algorithm (such as Clinger's algorithm),
// the resulting value will be _exactly_ equal to the original value.
// On most systems, this implies that using `strtod` to parse the
// output of `swift_dtoa_optimal_double` will yield exactly the
// original value.
//
// * Short. No other accurate result will have fewer digits.
//
// * Close. If there are multiple possible decimal forms that are
// both accurate and short, the form computed here will be
// closest to the original binary value.
//
// Notes:
//
// If the input value is infinity or NaN, or `digits_length < 17`, the
// function returns zero and generates no ouput.
//
// If the input value is zero, it will return `decimalExponent = 0` and
// a single digit of value zero.
//
int swift_decompose_double(double d,
int8_t *digits, size_t digits_length, int *decimalExponent);
// Naming: The `_p` forms take a `const void *` pointing to the value
// in memory. These forms do not require any support from the local C
// environment. In particular, they should work correctly even on
// systems with no floating-point support. Forms ending in a C
// floating-point type (e.g., "_float", "_double") are identical but
// take the corresponding argument type. These forms obviously
// require the C environment to support passing floating-point types as
// function arguments.

// Format a double as an ASCII string.
//
// For infinity, it outputs "inf" or "-inf".
//
// For NaN, it outputs a Swift-style detailed dump, including
// sign, signaling/quiet, and payload (if any). Typical output:
// "nan", "-nan", "-snan(0x1234)".
//
// For zero, it outputs "0.0" or "-0.0" depending on the sign.
//
// For other values, it uses `swift_decompose_double` to compute the
// digits, then uses either `swift_format_decimal` or
// `swift_format_exponential` to produce an ASCII string depending on
// the magnitude of the value.
//
// In all cases, it returns the number of ASCII characters actually
// written, or zero if the buffer was too small.
size_t swift_format_double(double, char *dest, size_t length);
#if SWIFT_DTOA_BINARY16_SUPPORT
size_t swift_dtoa_optimal_binary16_p(const void *, char *dest, size_t length);
#endif

#if SWIFT_DTOA_FLOAT16_SUPPORT
// See swift_decompose_double. `digits_length` must be at least 5.
int swift_decompose_float16(const __fp16 *f,
int8_t *digits, size_t digits_length, int *decimalExponent);
// See swift_format_double.
size_t swift_format_float16(const __fp16 *, char *dest, size_t length);
#if SWIFT_DTOA_BINARY32_SUPPORT
size_t swift_dtoa_optimal_binary32_p(const void *, char *dest, size_t length);
#if FLOAT_IS_BINARY32
// If `float` happens to be binary32, define the convenience wrapper.
size_t swift_dtoa_optimal_float(float, char *dest, size_t length);
#endif
#endif

#if SWIFT_DTOA_FLOAT_SUPPORT
// See swift_decompose_double. `digits_length` must be at least 9.
int swift_decompose_float(float f,
int8_t *digits, size_t digits_length, int *decimalExponent);
// See swift_format_double.
size_t swift_format_float(float, char *dest, size_t length);
#if SWIFT_DTOA_BINARY64_SUPPORT
size_t swift_dtoa_optimal_binary64_p(const void *, char *dest, size_t length);
#if DOUBLE_IS_BINARY64
// If `double` happens to be binary64, define the convenience wrapper.
size_t swift_dtoa_optimal_double(double, char *dest, size_t length);
#endif
#if LONG_DOUBLE_IS_BINARY64
// If `long double` happens to be binary64, define the convenience wrapper.
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
#endif
#endif

#if SWIFT_DTOA_FLOAT80_SUPPORT
// See swift_decompose_double. `digits_length` must be at least 21.
int swift_decompose_float80(long double f,
int8_t *digits, size_t digits_length, int *decimalExponent);
// See swift_format_double.
size_t swift_format_float80(long double, char *dest, size_t length);
#endif

// Generate an ASCII string from the raw exponent and digit information
// as generated by `swift_decompose_double`. Returns the number of
// bytes actually used. If `dest` was not big enough, these functions
// return zero. The generated string is always terminated with a zero
// byte unless `length` was zero.

// "Exponential" form uses common exponential format, e.g., "-1.234e+56"
// The exponent always has a sign and at least two digits. The
// generated string is never longer than `digits_count + 9` bytes,
// including the trailing zero byte.
size_t swift_format_exponential(char *dest, size_t length,
bool negative, const int8_t *digits, int digits_count, int decimalExponent);

// "Decimal" form writes the value without using exponents. This
// includes cases such as "0.000001234", "123.456", and "123456000.0".
// Note that the result always has a decimal point with at least one
// digit before and one digit after. The generated string is never
// longer than `digits_count + abs(exponent) + 4` bytes, including the
// trailing zero byte.
size_t swift_format_decimal(char *dest, size_t length,
bool negative, const int8_t *digits, int digits_count, int decimalExponent);
// Universal entry point works on all platforms, regardless of
// whether the local system has direct support for float80
size_t swift_dtoa_optimal_float80_p(const void *, char *dest, size_t length);
#if LONG_DOUBLE_IS_FLOAT80
// If 'long double' happens to be float80, define a convenience wrapper.
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
#endif
#endif

#if SWIFT_DTOA_BINARY128_SUPPORT
// Universal entry point works on all platforms, regardless of
// whether the local system has direct support for float80
size_t swift_dtoa_optimal_binary128_p(const void *, char *dest, size_t length);
#if LONG_DOUBLE_IS_BINARY128
// If 'long double' happens to be binary128, define a convenience wrapper.
size_t swift_dtoa_optimal_long_double(long double, char *dest, size_t length);
#endif
#endif

#ifdef __cplusplus
}
Expand Down
Loading