255 changes: 255 additions & 0 deletions libc/src/stdio/scanf_core/float_converter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
//===-- Int type specifier converters for scanf -----------------*- 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
//
//===----------------------------------------------------------------------===//

#include "src/stdio/scanf_core/float_converter.h"

#include "src/__support/CPP/limits.h"
#include "src/__support/char_vector.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/str_to_float.h"
#include "src/stdio/scanf_core/core_structs.h"
#include "src/stdio/scanf_core/reader.h"

#include <stddef.h>

namespace __llvm_libc {
namespace scanf_core {

constexpr char inline to_lower(char a) { return a | 32; }

void write_with_length(char *str, const FormatSection &to_conv) {
if ((to_conv.flags & NO_WRITE) != 0) {
return;
}

void *output_ptr = to_conv.output_ptr;

LengthModifier lm = to_conv.length_modifier;
switch (lm) {
case (LengthModifier::l): {
auto value = internal::strtofloatingpoint<double>(str, nullptr);
*reinterpret_cast<double *>(output_ptr) = value;
break;
}
case (LengthModifier::L): {
auto value = internal::strtofloatingpoint<long double>(str, nullptr);
*reinterpret_cast<long double *>(output_ptr) = value;
break;
}
default: {
auto value = internal::strtofloatingpoint<float>(str, nullptr);
*reinterpret_cast<float *>(output_ptr) = value;
break;
}
}
}

// All of the floating point conversions are the same for scanf, every name will
// accept every style.
int convert_float(Reader *reader, const FormatSection &to_conv) {
// %a/A/e/E/f/F/g/G "Matches an optionally signed floating-point number,
// infinity, or NaN, whose format is the same as expected for the subject
// sequence of the strtod function. The corresponding argument shall be a
// pointer to floating."

CharVector out_str = CharVector();
bool is_number = false;

size_t max_width = cpp::numeric_limits<size_t>::max();
if (to_conv.max_width > 0) {
max_width = to_conv.max_width;
}

char cur_char = reader->getc();
// Handle the sign.
if (cur_char == '+' || cur_char == '-') {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
if (out_str.length() == max_width) {
return MATCHING_FAILURE;
} else {
cur_char = reader->getc();
}
}

static constexpr char DECIMAL_POINT = '.';
static const char inf_string[] = "infinity";

// Handle inf

if (to_lower(cur_char) == inf_string[0]) {
size_t inf_index = 0;

for (; to_lower(cur_char) == inf_string[inf_index] &&
inf_index < sizeof(inf_string) && out_str.length() < max_width;
++inf_index) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
cur_char = reader->getc();
}

if (inf_index == 3 || inf_index == sizeof(inf_string) - 1) {
write_with_length(out_str.c_str(), to_conv);
return READ_OK;
} else {
return MATCHING_FAILURE;
}
}

static const char nan_string[] = "nan";

// Handle nan
if (to_lower(cur_char) == nan_string[0]) {
size_t nan_index = 0;

for (; to_lower(cur_char) == nan_string[nan_index] &&
nan_index < sizeof(nan_string) && out_str.length() < max_width;
++nan_index) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
cur_char = reader->getc();
}

if (nan_index == sizeof(nan_string) - 1) {
write_with_length(out_str.c_str(), to_conv);
return READ_OK;
} else {
return MATCHING_FAILURE;
}
}

// Assume base of 10 by default but check if it is actually base 16.
int base = 10;

// If the string starts with 0 it might be in hex.
if (cur_char == '0') {
is_number = true;
// Read the next character to check.
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
// If we've hit the end, then this is "0", which is valid.
if (out_str.length() == max_width) {
write_with_length(out_str.c_str(), to_conv);
return READ_OK;
} else {
cur_char = reader->getc();
}

// If that next character is an 'x' then this is a hexadecimal number.
if (to_lower(cur_char) == 'x') {
base = 16;

if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
// If we've hit the end here, we have "0x" which is a valid prefix to a
// floating point number, and will be evaluated to 0.
if (out_str.length() == max_width) {
write_with_length(out_str.c_str(), to_conv);
return READ_OK;
} else {
cur_char = reader->getc();
}
}
}

const char exponent_mark = ((base == 10) ? 'e' : 'p');
bool after_decimal = false;

// The format for the remaining characters at this point is DD.DDe+/-DD for
// base 10 and XX.XXp+/-DD for base 16

// This handles the digits before and after the decimal point, but not the
// exponent.
while (out_str.length() < max_width) {
if (internal::isalnum(cur_char) &&
internal::b36_char_to_int(cur_char) < base) {
is_number = true;
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
cur_char = reader->getc();
} else if (cur_char == DECIMAL_POINT && !after_decimal) {
after_decimal = true;
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
cur_char = reader->getc();
} else {
break;
}
}

// Handle the exponent, which has an exponent mark, an optional sign, and
// decimal digits.
if (to_lower(cur_char) == exponent_mark) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
if (out_str.length() == max_width) {
// This is laid out in the standard as being a matching error (100e is not
// a valid float) but may conflict with existing implementations.
return MATCHING_FAILURE;
} else {
cur_char = reader->getc();
}

if (cur_char == '+' || cur_char == '-') {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
if (out_str.length() == max_width) {
return MATCHING_FAILURE;
} else {
cur_char = reader->getc();
}
}

// It is specified by the standard that "100er" is a matching failure since
// the longest prefix of a possibly valid floating-point number (which is
// "100e") is not a valid floating-point number. If there is an exponent
// mark then there must be a digit after it else the number is not valid.
// Some implementations will roll back two characters (to just "100") and
// accept that since the prefix is not valid, and some will interpret an
// exponent mark followed by no digits as an additional exponent of 0
// (accepting "100e" and returning 100.0). Both of these behaviors are wrong
// by the standard, but they may be used in real code, see Hyrum's law. This
// code follows the standard, but may be incompatible due to code expecting
// these bugs.
if (!internal::isdigit(cur_char)) {
return MATCHING_FAILURE;
}

while (internal::isdigit(cur_char) && out_str.length() < max_width) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
cur_char = reader->getc();
}
}

// We always read one more character than will be used, so we have to put the
// last one back.
reader->ungetc(cur_char);

// If we haven't actually found any digits, this is a matching failure (this
// catches cases like "+.")
if (!is_number) {
return MATCHING_FAILURE;
}
write_with_length(out_str.c_str(), to_conv);

return READ_OK;
}

} // namespace scanf_core
} // namespace __llvm_libc
25 changes: 25 additions & 0 deletions libc/src/stdio/scanf_core/float_converter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===-- Float type specifier converter for scanf ----------------*- 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_STDIO_SCANF_CORE_FLOAT_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H

#include "src/stdio/scanf_core/core_structs.h"
#include "src/stdio/scanf_core/reader.h"

#include <stddef.h>

namespace __llvm_libc {
namespace scanf_core {

int convert_float(Reader *reader, const FormatSection &to_conv);

} // namespace scanf_core
} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_STDIO_SCANF_CORE_FLOAT_CONVERTER_H
2 changes: 1 addition & 1 deletion libc/src/stdio/scanf_core/int_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "src/stdio/scanf_core/string_converter.h"
#include "src/stdio/scanf_core/int_converter.h"

#include "src/__support/CPP/limits.h"
#include "src/__support/ctype_utils.h"
Expand Down
10 changes: 10 additions & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ add_libc_unittest(
libc.src.__support.fixedvector
)

add_libc_unittest(
char_vector_test
SUITE
libc_support_unittests
SRCS
char_vector_test.cpp
DEPENDS
libc.src.__support.char_vector
)

add_executable(
libc_str_to_float_comparison_test
str_to_float_comparison_test.cpp
Expand Down
3 changes: 1 addition & 2 deletions libc/test/src/__support/CPP/stringview_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//===-- Unittests for string_view
//------------------------------------------===//
//===-- Unittests for string_view -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
78 changes: 78 additions & 0 deletions libc/test/src/__support/char_vector_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===-- Unittests for char_vector ---------------------------------------===//
//
// 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/__support/char_vector.h"
#include "utils/UnitTest/Test.h"

using __llvm_libc::CharVector;

TEST(LlvmLibcCharVectorTest, InitializeCheck) {
CharVector v;
ASSERT_EQ(v.length(), size_t(0));
}

TEST(LlvmLibcCharVectorTest, AppendShort) {
CharVector v;
ASSERT_EQ(v.length(), size_t(0));

constexpr char test_str[] = "1234567890";
for (size_t i = 0; test_str[i] != '\0'; ++i) {
v.append(test_str[i]);
}
ASSERT_STREQ(v.c_str(), test_str);
}

TEST(LlvmLibcCharVectorTest, AppendMedium) {
CharVector v;
ASSERT_EQ(v.length(), size_t(0));

// 100 characters (each row is 50)
constexpr char test_str[] =
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy";
for (size_t i = 0; test_str[i] != '\0'; ++i) {
ASSERT_EQ(v.length(), i);
v.append(test_str[i]);
}
ASSERT_STREQ(v.c_str(), test_str);
ASSERT_EQ(v.length(), size_t(100));
}

TEST(LlvmLibcCharVectorTest, AppendLong) {
CharVector v;
ASSERT_EQ(v.length(), size_t(0));

// 1000 characters
constexpr char test_str[] =
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy"
"12345678901234567890123456789012345678901234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy";
for (size_t i = 0; test_str[i] != '\0'; ++i) {
ASSERT_EQ(v.length(), i);
v.append(test_str[i]);
}
ASSERT_STREQ(v.c_str(), test_str);
ASSERT_EQ(v.length(), size_t(1000));
}
2 changes: 2 additions & 0 deletions libc/test/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ add_libc_unittest(
sscanf_test.cpp
DEPENDS
libc.src.stdio.sscanf
LINK_LIBRARIES
LibcFPTestHelpers
)

add_libc_unittest(
Expand Down
363 changes: 363 additions & 0 deletions libc/test/src/stdio/sscanf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/limits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PlatformDefs.h"

#include "src/stdio/sscanf.h"

#include <stdio.h> // For EOF

#include "utils/UnitTest/FPMatcher.h"
#include "utils/UnitTest/Test.h"

TEST(LlvmLibcSScanfTest, SimpleStringConv) {
Expand Down Expand Up @@ -209,6 +213,365 @@ TEST(LlvmLibcSScanfTest, IntConvNoWriteTests) {
EXPECT_EQ(result, 0);
}

TEST(LlvmLibcSScanfTest, FloatConvSimple) {
int ret_val;
float result = 0;

float inf = __llvm_libc::fputil::FPBits<float>::inf().get_val();
float nan = __llvm_libc::fputil::FPBits<float>::build_nan(1);

ret_val = __llvm_libc::sscanf("123", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 123.0);

ret_val = __llvm_libc::sscanf("456.1", "%a", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 456.1);

ret_val = __llvm_libc::sscanf("0x789.ap0", "%e", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0x789.ap0);

ret_val = __llvm_libc::sscanf("0x.8", "%e", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0x0.8p0);

ret_val = __llvm_libc::sscanf("0x8.", "%e", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0x8.0p0);

ret_val = __llvm_libc::sscanf("+12.0e1", "%g", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 12.0e1);

ret_val = __llvm_libc::sscanf("inf", "%F", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, inf);

ret_val = __llvm_libc::sscanf("NaN", "%A", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, nan);

ret_val = __llvm_libc::sscanf("-InFiNiTy", "%E", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, -inf);

ret_val = __llvm_libc::sscanf("1e10", "%G", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 1e10);

ret_val = __llvm_libc::sscanf(".1", "%G", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.1);

ret_val = __llvm_libc::sscanf("1.", "%G", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 1.0);

ret_val = __llvm_libc::sscanf("0", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("Not a float", "%f", &result);
EXPECT_EQ(ret_val, 0);
}

TEST(LlvmLibcSScanfTest, FloatConvLengthModifier) {
int ret_val;
double d_result = 0;
long double ld_result = 0;

double d_inf = __llvm_libc::fputil::FPBits<double>::inf().get_val();
long double ld_nan = __llvm_libc::fputil::FPBits<long double>::build_nan(1);

ret_val = __llvm_libc::sscanf("123", "%lf", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(d_result, 123.0);

ret_val = __llvm_libc::sscanf("456.1", "%La", &ld_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(ld_result, 456.1L);

ret_val = __llvm_libc::sscanf("inf", "%le", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(d_result, d_inf);

ret_val = __llvm_libc::sscanf("nan", "%Lg", &ld_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(ld_result, ld_nan);

ret_val = __llvm_libc::sscanf("1e-300", "%lF", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(d_result, 1e-300);

ret_val = __llvm_libc::sscanf("1.0e600", "%LA", &ld_result);
EXPECT_EQ(ret_val, 1);
// 1e600 may be larger than the maximum long double (if long double is double).
// In that case both of these should be evaluated as inf.
#ifdef LONG_DOUBLE_IS_DOUBLE
EXPECT_FP_EQ(ld_result, d_inf);
#else
EXPECT_FP_EQ(ld_result, 1.0e600L);
#endif
}

TEST(LlvmLibcSScanfTest, FloatConvLongNumber) {
int ret_val;
float result = 0;
double d_result = 0;

// 32 characters
ret_val =
__llvm_libc::sscanf("123456789012345678901234567890.0", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 123456789012345678901234567890.0f);

// 64 characters
ret_val = __llvm_libc::sscanf(
"123456789012345678901234567890123456789012345678901234567890.000", "%la",
&d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(
d_result,
123456789012345678901234567890123456789012345678901234567890.000);

// 128 characters
ret_val = __llvm_libc::sscanf(
"123456789012345678901234567890123456789012345678901234567890"
"123456789012345678901234567890123456789012345678901234567890.0000000",
"%le", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(
d_result,
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.0000000);

// 256 characters
ret_val = __llvm_libc::sscanf("10000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"%lf", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(d_result, 1e255);

// 288 characters
ret_val = __llvm_libc::sscanf("10000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"%lf", &d_result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(d_result, 1e287);
}

TEST(LlvmLibcSScanfTest, FloatConvComplexParsing) {
int ret_val;
float result = 0;

float inf = __llvm_libc::fputil::FPBits<float>::inf().get_val();
float nan = __llvm_libc::fputil::FPBits<float>::build_nan(1);

ret_val = __llvm_libc::sscanf("0x1.0e3", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0x1.0e3p0);

ret_val = __llvm_libc::sscanf("", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("+", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("+.", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-.e+10", "%a", &result);
EXPECT_EQ(ret_val, 0);

// This is a specific example from the standard. Its behavior diverges from
// other implementations that accept "100e" as being the same as "100e0"
ret_val = __llvm_libc::sscanf("100er", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("nah", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("indirection", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("infnan", "%a", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, inf);

ret_val = __llvm_libc::sscanf("naninf", "%a", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, nan);

ret_val = __llvm_libc::sscanf("infinityinfinity", "%a", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, inf);

// For %f to accept a string as representing it has to be either "inf" or
// "infinity" when it stops. It only stops when it encounters a character that
// isn't the next one in the string, so it accepts "infi" as the the longest
// prefix of a possibly valid floating-point number, but determines that it is
// not valid and returns a matching failure. This is because it can only unget
// one character so when it finds that the character after the second 'i' is
// not the next character in "infinity" it can't rewind to the point where it
// had just "inf".
ret_val = __llvm_libc::sscanf("infi", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("infinite", "%a", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-.1e1", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, -.1e1);

ret_val = __llvm_libc::sscanf("1.2.e1", "%f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 1.2);
}

/*
TODO:
Max width tests
*/

TEST(LlvmLibcSScanfTest, FloatConvMaxWidth) {
int ret_val;
float result = 0;

float inf = __llvm_libc::fputil::FPBits<float>::inf().get_val();

ret_val = __llvm_libc::sscanf("123", "%3f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 123.0);

ret_val = __llvm_libc::sscanf("123", "%5f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 123.0);

ret_val = __llvm_libc::sscanf("456", "%1f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 4.0);

ret_val = __llvm_libc::sscanf("-789", "%1f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-123", "%2f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, -1.0);

ret_val = __llvm_libc::sscanf("inf", "%2f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("nan", "%1f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-inf", "%3f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("-nan", "%3f", &result);
EXPECT_EQ(ret_val, 0);

// If the max length were not here this would fail as discussed above, but
// since the max length limits it to the 3 it succeeds.
ret_val = __llvm_libc::sscanf("infinite", "%3f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, inf);

ret_val = __llvm_libc::sscanf("-infinite", "%4f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, -inf);

ret_val = __llvm_libc::sscanf("01", "%1f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("0x1", "%2f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("100e", "%4f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("100e+10", "%5f", &result);
EXPECT_EQ(ret_val, 0);

ret_val = __llvm_libc::sscanf("100e10", "%5f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 100e1);
}

TEST(LlvmLibcSScanfTest, FloatConvNoWrite) {
int ret_val;
float result = 0;

ret_val = __llvm_libc::sscanf("123", "%*f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("456.1", "%*a", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("0x789.ap0", "%*e", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("+12.0e1", "%*g", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("inf", "%*F", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("NaN", "%*A", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("-InFiNiTy", "%*E", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("1e10", "%*G", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf(".1", "%*G", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("123", "%*3f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("123", "%*5f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("456", "%*1f", &result);
EXPECT_EQ(ret_val, 1);
EXPECT_FP_EQ(result, 0.0);

ret_val = __llvm_libc::sscanf("Not a float", "%*f", &result);
EXPECT_EQ(ret_val, 0);
}

TEST(LlvmLibcSScanfTest, CombinedConv) {
int ret_val;
int result = 0;
Expand Down