mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Extend SwiftDtoa to provide optimal formatting for Float16 and use that for `Float16.description` and `Float16.debugDescription`. Notes on signaling NaNs: LLVM's Float16 support passes Float16s on x86 by legalizing to Float32. This works well for most purposes but incidentally loses the signaling marker from any NaN (because it's a conversion as far as the hardware is concerned), with a side effect that the print code never actually sees a true sNaN. This is similar to what happens with Float and Double on i386 backends. The earlier code here tried to detect sNaN in a different way, but that approach isn't guaranteed to work so we decided to make this code use the correct detection logic -- sNaN printing will just be broken until we can get a better argument passing convention. Resolves rdar://61414101
156 lines
5.7 KiB
C
156 lines
5.7 KiB
C
//===--- SwiftDtoa.h ---------------------------------------------*- c -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2018 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
|
|
//
|
|
//===---------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_DTOA_H
|
|
#define SWIFT_DTOA_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__)
|
|
// macOS and Linux support Float80 on X86 hardware but not on ARM
|
|
#if defined(__x86_64__) || defined(__i386)
|
|
#define SWIFT_DTOA_FLOAT80_SUPPORT 1
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#if SWIFT_DTOA_DOUBLE_SUPPORT
|
|
// Compute the optimal decimal digits and exponent for a double.
|
|
//
|
|
// Input:
|
|
// * `d` is the number to be decomposed
|
|
// * `digits` is an array of `digits_length`
|
|
// * `decimalExponent` is a pointer to an `int`
|
|
//
|
|
// 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
|
|
//
|
|
// 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.
|
|
//
|
|
// * 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);
|
|
|
|
// 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);
|
|
#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);
|
|
#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);
|
|
#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);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|