mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 02:38:07 +08:00
[libc] Templatize strtofloatingpoint and implement wcstof. (#167755)
This change follows the pattern of
315dfe5865 by making strtofloat also
accept wchar_t* strings
(in addition to regular char*). It uses overloads from wctype_utils or
specialized functions to ensure comparison with literal characters (or
literal strings) pick char or wchar_t variants based on the argument
type.
The wcstof implementation is added, with unit test cases copied from
strtof test suite.
This commit is contained in:
@@ -398,6 +398,7 @@ set(TARGET_LIBC_ENTRYPOINTS
|
||||
libc.src.wchar.wmemchr
|
||||
libc.src.wchar.wcpcpy
|
||||
libc.src.wchar.wcpncpy
|
||||
libc.src.wchar.wcstof
|
||||
libc.src.wchar.wcstok
|
||||
libc.src.wchar.wcstol
|
||||
libc.src.wchar.wcstoll
|
||||
|
||||
@@ -360,3 +360,10 @@ functions:
|
||||
- type: const wchar_t *__restrict
|
||||
- type: wchar_t **__restrict
|
||||
- type: int
|
||||
- name: wcstof
|
||||
standards:
|
||||
- stdc
|
||||
return_type: float
|
||||
arguments:
|
||||
- type: const wchar_t *__restrict
|
||||
- type: wchar_t **__restrict
|
||||
|
||||
@@ -221,7 +221,9 @@ add_header_library(
|
||||
HDRS
|
||||
high_precision_decimal.h
|
||||
DEPENDS
|
||||
.ctype_utils
|
||||
.str_to_integer
|
||||
.wctype_utils
|
||||
libc.hdr.stdint_proxy
|
||||
)
|
||||
|
||||
@@ -236,6 +238,7 @@ add_header_library(
|
||||
.str_to_integer
|
||||
.str_to_num_result
|
||||
.uint128
|
||||
.wctype_utils
|
||||
libc.hdr.errno_macros
|
||||
libc.hdr.stdint_proxy
|
||||
libc.src.__support.common
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "src/__support/ctype_utils.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
#include "src/__support/wctype_utils.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace internal {
|
||||
@@ -38,6 +39,24 @@ struct LShiftTableEntry {
|
||||
// TODO: Figure out where to put this.
|
||||
enum class RoundDirection { Up, Down, Nearest };
|
||||
|
||||
// These constants are used in both this file and in the main str_to_float.h.
|
||||
// TODO: Figure out where to put this.
|
||||
template <typename CharType> struct constants;
|
||||
template <> struct constants<char> {
|
||||
static constexpr char DECIMAL_POINT = '.';
|
||||
static constexpr char DECIMAL_EXPONENT_MARKER = 'e';
|
||||
static constexpr char HEX_EXPONENT_MARKER = 'p';
|
||||
static constexpr char INF_STRING[] = "infinity";
|
||||
static constexpr char NAN_STRING[] = "nan";
|
||||
};
|
||||
template <> struct constants<wchar_t> {
|
||||
static constexpr wchar_t DECIMAL_POINT = L'.';
|
||||
static constexpr wchar_t DECIMAL_EXPONENT_MARKER = L'e';
|
||||
static constexpr wchar_t HEX_EXPONENT_MARKER = L'p';
|
||||
static constexpr wchar_t INF_STRING[] = L"infinity";
|
||||
static constexpr wchar_t NAN_STRING[] = L"nan";
|
||||
};
|
||||
|
||||
// This is based on the HPD data structure described as part of the Simple
|
||||
// Decimal Conversion algorithm by Nigel Tao, described at this link:
|
||||
// https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
|
||||
@@ -314,9 +333,9 @@ private:
|
||||
public:
|
||||
// num_string is assumed to be a string of numeric characters. It doesn't
|
||||
// handle leading spaces.
|
||||
LIBC_INLINE
|
||||
HighPrecisionDecimal(
|
||||
const char *__restrict num_string,
|
||||
template <typename CharType>
|
||||
LIBC_INLINE HighPrecisionDecimal(
|
||||
const CharType *__restrict num_string,
|
||||
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
|
||||
bool saw_dot = false;
|
||||
size_t num_cur = 0;
|
||||
@@ -324,25 +343,26 @@ public:
|
||||
// them all.
|
||||
uint32_t total_digits = 0;
|
||||
while (num_cur < num_len &&
|
||||
(isdigit(num_string[num_cur]) || num_string[num_cur] == '.')) {
|
||||
if (num_string[num_cur] == '.') {
|
||||
(isdigit(num_string[num_cur]) ||
|
||||
num_string[num_cur] == constants<CharType>::DECIMAL_POINT)) {
|
||||
if (num_string[num_cur] == constants<CharType>::DECIMAL_POINT) {
|
||||
if (saw_dot) {
|
||||
break;
|
||||
}
|
||||
this->decimal_point = static_cast<int32_t>(total_digits);
|
||||
saw_dot = true;
|
||||
} else {
|
||||
if (num_string[num_cur] == '0' && this->num_digits == 0) {
|
||||
int digit = b36_char_to_int(num_string[num_cur]);
|
||||
if (digit == 0 && this->num_digits == 0) {
|
||||
--this->decimal_point;
|
||||
++num_cur;
|
||||
continue;
|
||||
}
|
||||
++total_digits;
|
||||
if (this->num_digits < MAX_NUM_DIGITS) {
|
||||
this->digits[this->num_digits] = static_cast<uint8_t>(
|
||||
internal::b36_char_to_int(num_string[num_cur]));
|
||||
this->digits[this->num_digits] = static_cast<uint8_t>(digit);
|
||||
++this->num_digits;
|
||||
} else if (num_string[num_cur] != '0') {
|
||||
} else if (digit != 0) {
|
||||
this->truncated = true;
|
||||
}
|
||||
}
|
||||
@@ -352,11 +372,10 @@ public:
|
||||
if (!saw_dot)
|
||||
this->decimal_point = static_cast<int32_t>(total_digits);
|
||||
|
||||
if (num_cur < num_len &&
|
||||
(num_string[num_cur] == 'e' || num_string[num_cur] == 'E')) {
|
||||
if (num_cur < num_len && tolower(num_string[num_cur]) ==
|
||||
constants<CharType>::DECIMAL_EXPONENT_MARKER) {
|
||||
++num_cur;
|
||||
if (isdigit(num_string[num_cur]) || num_string[num_cur] == '+' ||
|
||||
num_string[num_cur] == '-') {
|
||||
if (isdigit(num_string[num_cur]) || get_sign(num_string + num_cur) != 0) {
|
||||
auto result =
|
||||
strtointeger<int32_t>(num_string + num_cur, 10, num_len - num_cur);
|
||||
if (result.has_error()) {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "src/__support/str_to_integer.h"
|
||||
#include "src/__support/str_to_num_result.h"
|
||||
#include "src/__support/uint128.h"
|
||||
#include "src/__support/wctype_utils.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace internal {
|
||||
@@ -334,9 +335,9 @@ constexpr int32_t NUM_POWERS_OF_TWO =
|
||||
// the Eisel-Lemire algorithm fails, it's slower but more accurate. It's based
|
||||
// on the Simple Decimal Conversion algorithm by Nigel Tao, described at this
|
||||
// link: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
|
||||
template <class T>
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE FloatConvertReturn<T> simple_decimal_conversion(
|
||||
const char *__restrict numStart,
|
||||
const CharType *__restrict numStart,
|
||||
const size_t num_len = cpp::numeric_limits<size_t>::max(),
|
||||
RoundDirection round = RoundDirection::Nearest) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
@@ -676,12 +677,11 @@ template <> LIBC_INLINE constexpr int32_t get_lower_bound<double>() {
|
||||
// Takes a mantissa and base 10 exponent and converts it into its closest
|
||||
// floating point type T equivalient. First we try the Eisel-Lemire algorithm,
|
||||
// then if that fails then we fall back to a more accurate algorithm for
|
||||
// accuracy. The resulting mantissa and exponent are placed in outputMantissa
|
||||
// and outputExp2.
|
||||
template <class T>
|
||||
// accuracy.
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE FloatConvertReturn<T> decimal_exp_to_float(
|
||||
ExpandedFloat<T> init_num, bool truncated, RoundDirection round,
|
||||
const char *__restrict numStart,
|
||||
const CharType *__restrict numStart,
|
||||
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
using StorageType = typename FPBits::StorageType;
|
||||
@@ -860,36 +860,42 @@ LIBC_INLINE FloatConvertReturn<T> binary_exp_to_float(ExpandedFloat<T> init_num,
|
||||
return output;
|
||||
}
|
||||
|
||||
// checks if the next 4 characters of the string pointer are the start of a
|
||||
// Checks if the first characters of the string pointer are the start of a
|
||||
// hexadecimal floating point number. Does not advance the string pointer.
|
||||
LIBC_INLINE bool is_float_hex_start(const char *__restrict src,
|
||||
const char decimalPoint) {
|
||||
if (!(src[0] == '0' && tolower(src[1]) == 'x')) {
|
||||
template <typename CharType>
|
||||
LIBC_INLINE static bool is_float_hex_start(const CharType *__restrict src) {
|
||||
if (!is_char_or_wchar(src[0], '0', L'0') ||
|
||||
!is_char_or_wchar(tolower(src[1]), 'x', L'x')) {
|
||||
return false;
|
||||
}
|
||||
size_t first_digit = 2;
|
||||
if (src[2] == decimalPoint) {
|
||||
if (src[2] == constants<CharType>::DECIMAL_POINT) {
|
||||
++first_digit;
|
||||
}
|
||||
return isalnum(src[first_digit]) && b36_char_to_int(src[first_digit]) < 16;
|
||||
}
|
||||
|
||||
// Takes the start of a string representing a decimal float, as well as the
|
||||
// local decimalPoint. It returns if it suceeded in parsing any digits, and if
|
||||
// the return value is true then the outputs are pointer to the end of the
|
||||
// number, and the mantissa and exponent for the closest float T representation.
|
||||
// If the return value is false, then it is assumed that there is no number
|
||||
// here.
|
||||
template <class T>
|
||||
LIBC_INLINE StrToNumResult<ExpandedFloat<T>>
|
||||
decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
|
||||
RoundDirection round) {
|
||||
// Verifies that first prefix_len characters of str, when lowercased, match the
|
||||
// specified prefix.
|
||||
template <typename CharType>
|
||||
LIBC_INLINE static bool tolower_starts_with(const CharType *str,
|
||||
size_t prefix_len,
|
||||
const CharType *prefix) {
|
||||
for (size_t i = 0; i < prefix_len; ++i) {
|
||||
if (tolower(str[i]) != prefix[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempts parsing a decimal floating point number at the start of the string.
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE static StrToNumResult<ExpandedFloat<T>>
|
||||
decimal_string_to_float(const CharType *__restrict src, RoundDirection round) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
using StorageType = typename FPBits::StorageType;
|
||||
|
||||
constexpr uint32_t BASE = 10;
|
||||
constexpr char EXPONENT_MARKER = 'e';
|
||||
|
||||
bool truncated = false;
|
||||
bool seen_digit = false;
|
||||
bool after_decimal = false;
|
||||
@@ -926,7 +932,7 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
if (src[index] == DECIMAL_POINT) {
|
||||
if (src[index] == constants<CharType>::DECIMAL_POINT) {
|
||||
if (after_decimal) {
|
||||
break; // this means that src[index] points to a second decimal point,
|
||||
// ending the number.
|
||||
@@ -943,13 +949,10 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
|
||||
return output;
|
||||
|
||||
// TODO: When adding max length argument, handle the case of a trailing
|
||||
// EXPONENT MARKER, see scanf for more details.
|
||||
if (tolower(src[index]) == EXPONENT_MARKER) {
|
||||
bool has_sign = false;
|
||||
if (src[index + 1] == '+' || src[index + 1] == '-') {
|
||||
has_sign = true;
|
||||
}
|
||||
if (isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
|
||||
// exponent marker, see scanf for more details.
|
||||
if (tolower(src[index]) == constants<CharType>::DECIMAL_EXPONENT_MARKER) {
|
||||
int sign = get_sign(src + index + 1);
|
||||
if (isdigit(src[index + 1 + static_cast<size_t>(sign != 0)])) {
|
||||
++index;
|
||||
auto result = strtointeger<int32_t>(src + index, 10);
|
||||
if (result.has_error())
|
||||
@@ -985,22 +988,16 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
|
||||
return output;
|
||||
}
|
||||
|
||||
// Takes the start of a string representing a hexadecimal float, as well as the
|
||||
// local decimal point. It returns if it suceeded in parsing any digits, and if
|
||||
// the return value is true then the outputs are pointer to the end of the
|
||||
// number, and the mantissa and exponent for the closest float T representation.
|
||||
// If the return value is false, then it is assumed that there is no number
|
||||
// here.
|
||||
template <class T>
|
||||
LIBC_INLINE StrToNumResult<ExpandedFloat<T>>
|
||||
hexadecimal_string_to_float(const char *__restrict src,
|
||||
const char DECIMAL_POINT, RoundDirection round) {
|
||||
// Attempts parsing a hexadecimal floating point number at the start of the
|
||||
// string.
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE static StrToNumResult<ExpandedFloat<T>>
|
||||
hexadecimal_string_to_float(const CharType *__restrict src,
|
||||
RoundDirection round) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
using StorageType = typename FPBits::StorageType;
|
||||
|
||||
constexpr uint32_t BASE = 16;
|
||||
constexpr char EXPONENT_MARKER = 'p';
|
||||
|
||||
bool truncated = false;
|
||||
bool seen_digit = false;
|
||||
bool after_decimal = false;
|
||||
@@ -1038,7 +1035,7 @@ hexadecimal_string_to_float(const char *__restrict src,
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
if (src[index] == DECIMAL_POINT) {
|
||||
if (src[index] == constants<CharType>::DECIMAL_POINT) {
|
||||
if (after_decimal) {
|
||||
break; // this means that src[index] points to a second decimal point,
|
||||
// ending the number.
|
||||
@@ -1057,12 +1054,9 @@ hexadecimal_string_to_float(const char *__restrict src,
|
||||
// Convert the exponent from having a base of 16 to having a base of 2.
|
||||
exponent *= 4;
|
||||
|
||||
if (tolower(src[index]) == EXPONENT_MARKER) {
|
||||
bool has_sign = false;
|
||||
if (src[index + 1] == '+' || src[index + 1] == '-') {
|
||||
has_sign = true;
|
||||
}
|
||||
if (isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
|
||||
if (tolower(src[index]) == constants<CharType>::HEX_EXPONENT_MARKER) {
|
||||
int sign = get_sign(src + index + 1);
|
||||
if (isdigit(src[index + 1 + static_cast<size_t>(sign != 0)])) {
|
||||
++index;
|
||||
auto result = strtointeger<int32_t>(src + index, 10);
|
||||
if (result.has_error())
|
||||
@@ -1098,21 +1092,21 @@ hexadecimal_string_to_float(const char *__restrict src,
|
||||
return output;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE typename fputil::FPBits<T>::StorageType
|
||||
nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
|
||||
nan_mantissa_from_ncharseq(const CharType *str, size_t len) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
using StorageType = typename FPBits::StorageType;
|
||||
|
||||
StorageType nan_mantissa = 0;
|
||||
|
||||
if (ncharseq.data() != nullptr && isdigit(ncharseq[0])) {
|
||||
if (len > 0 && isdigit(str[0])) {
|
||||
StrToNumResult<StorageType> strtoint_result =
|
||||
strtointeger<StorageType>(ncharseq.data(), 0);
|
||||
strtointeger<StorageType>(str, 0, len);
|
||||
if (!strtoint_result.has_error())
|
||||
nan_mantissa = strtoint_result.value;
|
||||
|
||||
if (strtoint_result.parsed_len != static_cast<ptrdiff_t>(ncharseq.size()))
|
||||
if (strtoint_result.parsed_len != static_cast<ptrdiff_t>(len))
|
||||
nan_mantissa = 0;
|
||||
}
|
||||
|
||||
@@ -1123,59 +1117,44 @@ nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
|
||||
// is used as the backend for all of the string to float functions.
|
||||
// TODO: Add src_len member to match strtointeger.
|
||||
// TODO: Next, move from char* and length to string_view
|
||||
template <class T>
|
||||
LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE StrToNumResult<T>
|
||||
strtofloatingpoint(const CharType *__restrict src) {
|
||||
using FPBits = typename fputil::FPBits<T>;
|
||||
using StorageType = typename FPBits::StorageType;
|
||||
|
||||
FPBits result = FPBits();
|
||||
bool seen_digit = false;
|
||||
char sign = '+';
|
||||
|
||||
int error = 0;
|
||||
|
||||
size_t index = first_non_whitespace(src);
|
||||
int sign = get_sign(src + index);
|
||||
bool is_positive = (sign >= 0);
|
||||
index += (sign != 0);
|
||||
|
||||
if (src[index] == '+' || src[index] == '-') {
|
||||
sign = src[index];
|
||||
++index;
|
||||
}
|
||||
|
||||
if (sign == '-') {
|
||||
if (sign < 0) {
|
||||
result.set_sign(Sign::NEG);
|
||||
}
|
||||
|
||||
static constexpr char DECIMAL_POINT = '.';
|
||||
static const char *inf_string = "infinity";
|
||||
static const char *nan_string = "nan";
|
||||
|
||||
if (isdigit(src[index]) || src[index] == DECIMAL_POINT) { // regular number
|
||||
if (isdigit(src[index]) ||
|
||||
src[index] == constants<CharType>::DECIMAL_POINT) { // regular number
|
||||
int base = 10;
|
||||
if (is_float_hex_start(src + index, DECIMAL_POINT)) {
|
||||
if (is_float_hex_start(src + index)) {
|
||||
base = 16;
|
||||
index += 2;
|
||||
seen_digit = true;
|
||||
}
|
||||
|
||||
RoundDirection round_direction = RoundDirection::Nearest;
|
||||
|
||||
switch (fputil::quick_get_round()) {
|
||||
case FE_TONEAREST:
|
||||
round_direction = RoundDirection::Nearest;
|
||||
break;
|
||||
case FE_UPWARD:
|
||||
if (sign == '+') {
|
||||
round_direction = RoundDirection::Up;
|
||||
} else {
|
||||
round_direction = RoundDirection::Down;
|
||||
}
|
||||
round_direction = is_positive ? RoundDirection::Up : RoundDirection::Down;
|
||||
break;
|
||||
case FE_DOWNWARD:
|
||||
if (sign == '+') {
|
||||
round_direction = RoundDirection::Down;
|
||||
} else {
|
||||
round_direction = RoundDirection::Up;
|
||||
}
|
||||
round_direction = is_positive ? RoundDirection::Down : RoundDirection::Up;
|
||||
break;
|
||||
case FE_TOWARDZERO:
|
||||
round_direction = RoundDirection::Down;
|
||||
@@ -1184,58 +1163,53 @@ LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
|
||||
|
||||
StrToNumResult<ExpandedFloat<T>> parse_result({0, 0});
|
||||
if (base == 16) {
|
||||
parse_result = hexadecimal_string_to_float<T>(src + index, DECIMAL_POINT,
|
||||
round_direction);
|
||||
parse_result =
|
||||
hexadecimal_string_to_float<T>(src + index, round_direction);
|
||||
} else { // base is 10
|
||||
parse_result = decimal_string_to_float<T>(src + index, DECIMAL_POINT,
|
||||
round_direction);
|
||||
parse_result = decimal_string_to_float<T>(src + index, round_direction);
|
||||
}
|
||||
seen_digit = parse_result.parsed_len != 0;
|
||||
result.set_mantissa(parse_result.value.mantissa);
|
||||
result.set_biased_exponent(parse_result.value.exponent);
|
||||
index += parse_result.parsed_len;
|
||||
error = parse_result.error;
|
||||
} else if (tolower(src[index]) == 'n') { // NaN
|
||||
if (tolower(src[index + 1]) == nan_string[1] &&
|
||||
tolower(src[index + 2]) == nan_string[2]) {
|
||||
seen_digit = true;
|
||||
index += 3;
|
||||
StorageType nan_mantissa = 0;
|
||||
// this handles the case of `NaN(n-character-sequence)`, where the
|
||||
// n-character-sequence is made of 0 or more letters, numbers, or
|
||||
// underscore characters in any order.
|
||||
if (src[index] == '(') {
|
||||
size_t left_paren = index;
|
||||
} else if (tolower_starts_with(src + index, 3,
|
||||
constants<CharType>::NAN_STRING)) {
|
||||
// NAN
|
||||
seen_digit = true;
|
||||
index += 3;
|
||||
StorageType nan_mantissa = 0;
|
||||
// this handles the case of `NaN(n-character-sequence)`, where the
|
||||
// n-character-sequence is made of 0 or more letters, numbers, or
|
||||
// underscore characters in any order.
|
||||
if (is_char_or_wchar(src[index], '(', L'(')) {
|
||||
size_t left_paren = index;
|
||||
++index;
|
||||
while (isalnum(src[index]) || is_char_or_wchar(src[index], '_', L'_'))
|
||||
++index;
|
||||
while (isalnum(src[index]) || src[index] == '_')
|
||||
++index;
|
||||
if (src[index] == ')') {
|
||||
++index;
|
||||
nan_mantissa = nan_mantissa_from_ncharseq<T>(
|
||||
cpp::string_view(src + (left_paren + 1), index - left_paren - 2));
|
||||
} else {
|
||||
index = left_paren;
|
||||
}
|
||||
}
|
||||
result = FPBits(result.quiet_nan(result.sign(), nan_mantissa));
|
||||
}
|
||||
} else if (tolower(src[index]) == 'i') { // INF
|
||||
if (tolower(src[index + 1]) == inf_string[1] &&
|
||||
tolower(src[index + 2]) == inf_string[2]) {
|
||||
seen_digit = true;
|
||||
result = FPBits(result.inf(result.sign()));
|
||||
if (tolower(src[index + 3]) == inf_string[3] &&
|
||||
tolower(src[index + 4]) == inf_string[4] &&
|
||||
tolower(src[index + 5]) == inf_string[5] &&
|
||||
tolower(src[index + 6]) == inf_string[6] &&
|
||||
tolower(src[index + 7]) == inf_string[7]) {
|
||||
// if the string is "INFINITY" then consume 8 characters.
|
||||
index += 8;
|
||||
if (is_char_or_wchar(src[index], ')', L')')) {
|
||||
++index;
|
||||
nan_mantissa = nan_mantissa_from_ncharseq<T>(src + (left_paren + 1),
|
||||
index - left_paren - 2);
|
||||
} else {
|
||||
index += 3;
|
||||
index = left_paren;
|
||||
}
|
||||
}
|
||||
result = FPBits(result.quiet_nan(result.sign(), nan_mantissa));
|
||||
} else if (tolower_starts_with(src + index, 8,
|
||||
constants<CharType>::INF_STRING)) {
|
||||
// INFINITY
|
||||
seen_digit = true;
|
||||
result = FPBits(result.inf(result.sign()));
|
||||
index += 8;
|
||||
} else if (tolower_starts_with(src + index, 3,
|
||||
constants<CharType>::INF_STRING)) {
|
||||
// INF
|
||||
seen_digit = true;
|
||||
result = FPBits(result.inf(result.sign()));
|
||||
index += 3;
|
||||
}
|
||||
|
||||
if (!seen_digit) { // If there is nothing to actually parse, then return 0.
|
||||
return {T(0), 0, error};
|
||||
}
|
||||
@@ -1262,7 +1236,7 @@ template <class T> LIBC_INLINE StrToNumResult<T> strtonan(const char *arg) {
|
||||
++index;
|
||||
|
||||
if (arg[index] == '\0')
|
||||
nan_mantissa = nan_mantissa_from_ncharseq<T>(cpp::string_view(arg, index));
|
||||
nan_mantissa = nan_mantissa_from_ncharseq<T>(arg, index);
|
||||
|
||||
result = FPBits::quiet_nan(Sign::POS, nan_mantissa);
|
||||
return {result.get_val(), 0, error};
|
||||
|
||||
@@ -99,6 +99,17 @@ add_entrypoint_object(
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
wcstof
|
||||
SRCS
|
||||
wcstof.cpp
|
||||
HDRS
|
||||
wcstof.h
|
||||
DEPENDS
|
||||
libc.src.__support.str_to_float
|
||||
libc.src.errno.errno
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
wcstok
|
||||
SRCS
|
||||
|
||||
30
libc/src/wchar/wcstof.cpp
Normal file
30
libc/src/wchar/wcstof.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//===-- Implementation of wcstof ------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/wchar/wcstof.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/str_to_float.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(float, wcstof,
|
||||
(const wchar_t *__restrict str,
|
||||
wchar_t **__restrict str_end)) {
|
||||
auto result = internal::strtofloatingpoint<float>(str);
|
||||
if (result.has_error())
|
||||
libc_errno = result.error;
|
||||
|
||||
if (str_end != nullptr)
|
||||
*str_end = const_cast<wchar_t *>(str + result.parsed_len);
|
||||
|
||||
return result.value;
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
21
libc/src/wchar/wcstof.h
Normal file
21
libc/src/wchar/wcstof.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//===-- Implementation header for wcstof ------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC_WCHAR_WCSTOF_H
|
||||
#define LLVM_LIBC_SRC_WCHAR_WCSTOF_H
|
||||
|
||||
#include "hdr/types/wchar_t.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
float wcstof(const wchar_t *__restrict str, wchar_t **__restrict str_end);
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC_WCHAR_WCSTOF_H
|
||||
@@ -528,3 +528,15 @@ add_libc_test(
|
||||
libc.src.wchar.wcstoull
|
||||
.wcstol_test_support
|
||||
)
|
||||
|
||||
add_libc_test(
|
||||
wcstof_test
|
||||
SUITE
|
||||
libc_wchar_unittests
|
||||
SRCS
|
||||
wcstof_test.cpp
|
||||
DEPENDS
|
||||
libc.src.wchar.wcstof
|
||||
libc.test.UnitTest.ErrnoCheckingTest
|
||||
libc.test.UnitTest.LibcFPTestHelpers
|
||||
)
|
||||
|
||||
199
libc/test/src/wchar/wcstof_test.cpp
Normal file
199
libc/test/src/wchar/wcstof_test.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
//===-- Unittests for wcstof ----------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/wchar/wcstof.h"
|
||||
|
||||
#include "test/UnitTest/ErrnoCheckingTest.h"
|
||||
#include "test/UnitTest/FPMatcher.h"
|
||||
#include "test/UnitTest/RoundingModeUtils.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
|
||||
using LIBC_NAMESPACE::fputil::testing::ForceRoundingModeTest;
|
||||
using LIBC_NAMESPACE::fputil::testing::RoundingMode;
|
||||
|
||||
class LlvmLibcWcstofTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest,
|
||||
ForceRoundingModeTest<RoundingMode::Nearest> {
|
||||
public:
|
||||
void run_test(const wchar_t *inputString, const ptrdiff_t expectedStrLen,
|
||||
const uint32_t expectedRawData, const int expectedErrno = 0) {
|
||||
// expectedRawData is the expected float result as a uint32_t, organized
|
||||
// according to IEEE754:
|
||||
//
|
||||
// +-- 1 Sign Bit +-- 23 Mantissa bits
|
||||
// | |
|
||||
// | +----------+----------+
|
||||
// | | |
|
||||
// SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
|
||||
// | |
|
||||
// +--+---+
|
||||
// |
|
||||
// +-- 8 Exponent Bits
|
||||
//
|
||||
// This is so that the result can be compared in parts.
|
||||
wchar_t *str_end = nullptr;
|
||||
|
||||
LIBC_NAMESPACE::fputil::FPBits<float> expected_fp =
|
||||
LIBC_NAMESPACE::fputil::FPBits<float>(expectedRawData);
|
||||
|
||||
float result = LIBC_NAMESPACE::wcstof(inputString, &str_end);
|
||||
|
||||
EXPECT_EQ(str_end - inputString, expectedStrLen);
|
||||
EXPECT_FP_EQ(result, expected_fp.get_val());
|
||||
ASSERT_ERRNO_EQ(expectedErrno);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, BasicDecimalTests) {
|
||||
run_test(L"1", 1, 0x3f800000);
|
||||
run_test(L"123", 3, 0x42f60000);
|
||||
run_test(L"1234567890", 10, 0x4e932c06u);
|
||||
run_test(L"123456789012345678901", 21, 0x60d629d4);
|
||||
run_test(L"0.1", 3, 0x3dcccccdu);
|
||||
run_test(L".1", 2, 0x3dcccccdu);
|
||||
run_test(L"-0.123456789", 12, 0xbdfcd6eau);
|
||||
run_test(L"0.11111111111111111111", 22, 0x3de38e39u);
|
||||
run_test(L"0.0000000000000000000000001", 27, 0x15f79688u);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, DecimalOutOfRangeTests) {
|
||||
run_test(L"555E36", 6, 0x7f800000, ERANGE);
|
||||
run_test(L"1e-10000", 8, 0x0, ERANGE);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, DecimalsWithRoundingProblems) {
|
||||
run_test(L"20040229", 8, 0x4b98e512);
|
||||
run_test(L"20040401", 8, 0x4b98e568);
|
||||
run_test(L"9E9", 3, 0x50061c46);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, DecimalSubnormals) {
|
||||
run_test(L"1.4012984643248170709237295832899161312802619418765e-45", 55, 0x1,
|
||||
ERANGE);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, DecimalWithLongExponent) {
|
||||
run_test(L"1e2147483648", 12, 0x7f800000, ERANGE);
|
||||
run_test(L"1e2147483646", 12, 0x7f800000, ERANGE);
|
||||
run_test(L"100e2147483646", 14, 0x7f800000, ERANGE);
|
||||
run_test(L"1e-2147483647", 13, 0x0, ERANGE);
|
||||
run_test(L"1e-2147483649", 13, 0x0, ERANGE);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, BasicHexadecimalTests) {
|
||||
run_test(L"0x1", 3, 0x3f800000);
|
||||
run_test(L"0x10", 4, 0x41800000);
|
||||
run_test(L"0x11", 4, 0x41880000);
|
||||
run_test(L"0x0.1234", 8, 0x3d91a000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, HexadecimalSubnormalTests) {
|
||||
run_test(L"0x0.0000000000000000000000000000000002", 38, 0x4000, ERANGE);
|
||||
|
||||
// This is the largest subnormal number as represented in hex
|
||||
run_test(L"0x0.00000000000000000000000000000003fffff8", 42, 0x7fffff, ERANGE);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, HexadecimalSubnormalRoundingTests) {
|
||||
// This is the largest subnormal number that gets rounded down to 0 (as a
|
||||
// float)
|
||||
run_test(L"0x0.00000000000000000000000000000000000004", 42, 0x0, ERANGE);
|
||||
|
||||
// This is slightly larger, and thus rounded up
|
||||
run_test(L"0x0.000000000000000000000000000000000000041", 43, 0x00000001,
|
||||
ERANGE);
|
||||
|
||||
// These check that we're rounding to even properly
|
||||
run_test(L"0x0.0000000000000000000000000000000000000b", 42, 0x00000001,
|
||||
ERANGE);
|
||||
run_test(L"0x0.0000000000000000000000000000000000000c", 42, 0x00000002,
|
||||
ERANGE);
|
||||
|
||||
// These check that we're rounding to even properly even when the input bits
|
||||
// are longer than the bit fields can contain.
|
||||
run_test(L"0x1.000000000000000000000p-150", 30, 0x00000000, ERANGE);
|
||||
run_test(L"0x1.000010000000000001000p-150", 30, 0x00000001, ERANGE);
|
||||
run_test(L"0x1.000100000000000001000p-134", 30, 0x00008001, ERANGE);
|
||||
run_test(L"0x1.FFFFFC000000000001000p-127", 30, 0x007FFFFF, ERANGE);
|
||||
run_test(L"0x1.FFFFFE000000000000000p-127", 30, 0x00800000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, HexadecimalNormalRoundingTests) {
|
||||
// This also checks the round to even behavior by checking three adjacent
|
||||
// numbers.
|
||||
// This gets rounded down to even
|
||||
run_test(L"0x123456500", 11, 0x4f91a2b2);
|
||||
// This doesn't get rounded at all
|
||||
run_test(L"0x123456600", 11, 0x4f91a2b3);
|
||||
// This gets rounded up to even
|
||||
run_test(L"0x123456700", 11, 0x4f91a2b4);
|
||||
// Correct rounding for long input
|
||||
run_test(L"0x1.000001000000000000000", 25, 0x3f800000);
|
||||
run_test(L"0x1.000001000000000000100", 25, 0x3f800001);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, HexadecimalsWithRoundingProblems) {
|
||||
run_test(L"0xFFFFFFFF", 10, 0x4f800000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, HexadecimalOutOfRangeTests) {
|
||||
run_test(L"0x123456789123456789123456789123456789", 38, 0x7f800000, ERANGE);
|
||||
run_test(L"-0x123456789123456789123456789123456789", 39, 0xff800000, ERANGE);
|
||||
run_test(L"0x0.00000000000000000000000000000000000001", 42, 0x0, ERANGE);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, InfTests) {
|
||||
run_test(L"INF", 3, 0x7f800000);
|
||||
run_test(L"INFinity", 8, 0x7f800000);
|
||||
run_test(L"infnity", 3, 0x7f800000);
|
||||
run_test(L"infinit", 3, 0x7f800000);
|
||||
run_test(L"infinfinit", 3, 0x7f800000);
|
||||
run_test(L"innf", 0, 0x0);
|
||||
run_test(L"-inf", 4, 0xff800000);
|
||||
run_test(L"-iNfInItY", 9, 0xff800000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, SimpleNaNTests) {
|
||||
run_test(L"NaN", 3, 0x7fc00000);
|
||||
run_test(L"-nAn", 4, 0xffc00000);
|
||||
}
|
||||
|
||||
// These NaNs are of the form `NaN(n-character-sequence)` where the
|
||||
// n-character-sequence is 0 or more letters or numbers. If there is anything
|
||||
// other than a letter or a number, then the valid number is just `NaN`. If
|
||||
// the sequence is valid, then the interpretation of them is implementation
|
||||
// defined, in this case it's passed to strtoll with an automatic base, and
|
||||
// the result is put into the mantissa if it takes up the whole width of the
|
||||
// parentheses.
|
||||
TEST_F(LlvmLibcWcstofTest, NaNWithParenthesesEmptyTest) {
|
||||
run_test(L"NaN()", 5, 0x7fc00000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, NaNWithParenthesesValidNumberTests) {
|
||||
run_test(L"NaN(1234)", 9, 0x7fc004d2);
|
||||
run_test(L"NaN(0x1234)", 11, 0x7fc01234);
|
||||
run_test(L"NaN(01234)", 10, 0x7fc0029c);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, NaNWithParenthesesInvalidSequenceTests) {
|
||||
run_test(L"NaN( 1234)", 3, 0x7fc00000);
|
||||
run_test(L"NaN(-1234)", 3, 0x7fc00000);
|
||||
run_test(L"NaN(asd&f)", 3, 0x7fc00000);
|
||||
run_test(L"NaN(123 )", 3, 0x7fc00000);
|
||||
run_test(L"NaN(123+asdf)", 3, 0x7fc00000);
|
||||
run_test(L"NaN(123", 3, 0x7fc00000);
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcWcstofTest, NaNWithParenthesesValidSequenceInvalidNumberTests) {
|
||||
run_test(L"NaN(1a)", 7, 0x7fc00000);
|
||||
run_test(L"NaN(asdf)", 9, 0x7fc00000);
|
||||
run_test(L"NaN(1A1)", 8, 0x7fc00000);
|
||||
run_test(L"NaN(underscores_are_ok)", 23, 0x7fc00000);
|
||||
run_test(
|
||||
L"NaN(1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_)",
|
||||
68, 0x7fc00000);
|
||||
}
|
||||
@@ -1025,6 +1025,7 @@ libc_support_library(
|
||||
":__support_str_to_integer",
|
||||
":__support_str_to_num_result",
|
||||
":__support_uint128",
|
||||
":__support_wctype_utils",
|
||||
":hdr_errno_macros",
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user