Fix sscanf/wcstod parsing of NaNs.
The parsefloat routines -- which let us pass NaNs and infinities on to
strto(f|d|ld) -- come from NetBSD.
Also fix LP64's strtold to return a NaN, and fix all the architectures
to return quiet NaNs.
Also fix wcstof/wcstod/wcstold to use parsefloat so they support hex
floats.
Lots of new tests.
Bug: http://b/31101647
Change-Id: Id7d46ac2d8acb8770b5e8c445e87cfabfde6f111
diff --git a/tests/math_data_test.h b/tests/math_data_test.h
index 0aba701..42dc42c 100644
--- a/tests/math_data_test.h
+++ b/tests/math_data_test.h
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include <math.h>
#include <fenv.h>
template <typename RT, typename T1>
@@ -102,7 +103,28 @@
uint64_t sign_magnitude;
};
-// TODO: long double.
+template <> union fp_u<long double> {
+ long double value;
+#if defined(__LP64__)
+ struct {
+ unsigned fracl;
+ unsigned fraclm;
+ unsigned frachm;
+ unsigned frach:16;
+ unsigned exp:15;
+ unsigned sign:1;
+ } bits;
+ __int128_t sign_magnitude;
+#else
+ struct {
+ unsigned fracl;
+ unsigned frach:20;
+ unsigned exp:11;
+ unsigned sign:1;
+ } bits;
+ uint64_t sign_magnitude;
+#endif
+};
template <typename T>
static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) {
@@ -134,14 +156,8 @@
return ::testing::AssertionSuccess();
}
- // Output the actual and expected values as hex floating point.
- char expected_str[64];
- char actual_str[64];
- snprintf(expected_str, sizeof(expected_str), "%a", expected);
- snprintf(actual_str, sizeof(actual_str), "%a", actual);
-
return ::testing::AssertionFailure()
- << "expected (" << expected_str << ") != actual (" << actual_str << ")";
+ << "expected (" << std::hexfloat << expected << ") != actual (" << actual << ")";
}
};
@@ -282,4 +298,3 @@
data[i].expected, f(data[i].input1, data[i].input2, data[i].input3)) << "Failed on element " << i;
}
}
-
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index ecba4ad..79c5e92 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -402,51 +402,135 @@
}
template <typename T>
-void CheckInfNan(int snprintf_fn(T*, size_t, const T*, ...),
- const T* fmt, const T* fmt_plus,
- const T* minus_inf, const T* inf_, const T* plus_inf,
- const T* minus_nan, const T* nan_, const T* plus_nan) {
+static void CheckInfNan(int snprintf_fn(T*, size_t, const T*, ...),
+ int sscanf_fn(const T*, const T*, ...),
+ const T* fmt_string, const T* fmt, const T* fmt_plus,
+ const T* minus_inf, const T* inf_, const T* plus_inf,
+ const T* minus_nan, const T* nan_, const T* plus_nan) {
T buf[BUFSIZ];
+ float f;
- snprintf_fn(buf, sizeof(buf), fmt, nan(""));
+ // NaN.
+
+ snprintf_fn(buf, sizeof(buf), fmt, nanf(""));
EXPECT_STREQ(nan_, buf) << fmt;
- snprintf_fn(buf, sizeof(buf), fmt, -nan(""));
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_TRUE(isnan(f));
+
+ snprintf_fn(buf, sizeof(buf), fmt, -nanf(""));
EXPECT_STREQ(minus_nan, buf) << fmt;
- snprintf_fn(buf, sizeof(buf), fmt_plus, nan(""));
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_TRUE(isnan(f));
+
+ snprintf_fn(buf, sizeof(buf), fmt_plus, nanf(""));
EXPECT_STREQ(plus_nan, buf) << fmt_plus;
- snprintf_fn(buf, sizeof(buf), fmt_plus, -nan(""));
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_TRUE(isnan(f));
+
+ snprintf_fn(buf, sizeof(buf), fmt_plus, -nanf(""));
EXPECT_STREQ(minus_nan, buf) << fmt_plus;
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_TRUE(isnan(f));
- snprintf_fn(buf, sizeof(buf), fmt, HUGE_VAL);
+ // Inf.
+
+ snprintf_fn(buf, sizeof(buf), fmt, HUGE_VALF);
EXPECT_STREQ(inf_, buf) << fmt;
- snprintf_fn(buf, sizeof(buf), fmt, -HUGE_VAL);
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_EQ(HUGE_VALF, f);
+
+ snprintf_fn(buf, sizeof(buf), fmt, -HUGE_VALF);
EXPECT_STREQ(minus_inf, buf) << fmt;
- snprintf_fn(buf, sizeof(buf), fmt_plus, HUGE_VAL);
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_EQ(-HUGE_VALF, f);
+
+ snprintf_fn(buf, sizeof(buf), fmt_plus, HUGE_VALF);
EXPECT_STREQ(plus_inf, buf) << fmt_plus;
- snprintf_fn(buf, sizeof(buf), fmt_plus, -HUGE_VAL);
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_EQ(HUGE_VALF, f);
+
+ snprintf_fn(buf, sizeof(buf), fmt_plus, -HUGE_VALF);
EXPECT_STREQ(minus_inf, buf) << fmt_plus;
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+ EXPECT_EQ(-HUGE_VALF, f);
+
+ // Check case-insensitivity.
+ snprintf_fn(buf, sizeof(buf), fmt_string, "[InFiNiTy]");
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f)) << buf;
+ EXPECT_EQ(HUGE_VALF, f);
+ snprintf_fn(buf, sizeof(buf), fmt_string, "[NaN]");
+ EXPECT_EQ(1, sscanf_fn(buf, fmt, &f)) << buf;
+ EXPECT_TRUE(isnan(f));
}
-TEST(STDIO_TEST, snprintf_inf_nan) {
- CheckInfNan(snprintf, "%a", "%+a", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
- CheckInfNan(snprintf, "%A", "%+A", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
- CheckInfNan(snprintf, "%e", "%+e", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
- CheckInfNan(snprintf, "%E", "%+E", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
- CheckInfNan(snprintf, "%f", "%+f", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
- CheckInfNan(snprintf, "%F", "%+F", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
- CheckInfNan(snprintf, "%g", "%+g", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
- CheckInfNan(snprintf, "%G", "%+G", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+TEST(STDIO_TEST, snprintf_sscanf_inf_nan) {
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%a]", "[%+a]",
+ "[-inf]", "[inf]", "[+inf]",
+ "[-nan]", "[nan]", "[+nan]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%A]", "[%+A]",
+ "[-INF]", "[INF]", "[+INF]",
+ "[-NAN]", "[NAN]", "[+NAN]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%e]", "[%+e]",
+ "[-inf]", "[inf]", "[+inf]",
+ "[-nan]", "[nan]", "[+nan]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%E]", "[%+E]",
+ "[-INF]", "[INF]", "[+INF]",
+ "[-NAN]", "[NAN]", "[+NAN]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%f]", "[%+f]",
+ "[-inf]", "[inf]", "[+inf]",
+ "[-nan]", "[nan]", "[+nan]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%F]", "[%+F]",
+ "[-INF]", "[INF]", "[+INF]",
+ "[-NAN]", "[NAN]", "[+NAN]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%g]", "[%+g]",
+ "[-inf]", "[inf]", "[+inf]",
+ "[-nan]", "[nan]", "[+nan]");
+ CheckInfNan(snprintf, sscanf, "%s",
+ "[%G]", "[%+G]",
+ "[-INF]", "[INF]", "[+INF]",
+ "[-NAN]", "[NAN]", "[+NAN]");
}
-TEST(STDIO_TEST, wsprintf_inf_nan) {
- CheckInfNan(swprintf, L"%a", L"%+a", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
- CheckInfNan(swprintf, L"%A", L"%+A", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
- CheckInfNan(swprintf, L"%e", L"%+e", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
- CheckInfNan(swprintf, L"%E", L"%+E", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
- CheckInfNan(swprintf, L"%f", L"%+f", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
- CheckInfNan(swprintf, L"%F", L"%+F", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
- CheckInfNan(swprintf, L"%g", L"%+g", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
- CheckInfNan(swprintf, L"%G", L"%+G", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
+TEST(STDIO_TEST, swprintf_swscanf_inf_nan) {
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%a]", L"[%+a]",
+ L"[-inf]", L"[inf]", L"[+inf]",
+ L"[-nan]", L"[nan]", L"[+nan]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%A]", L"[%+A]",
+ L"[-INF]", L"[INF]", L"[+INF]",
+ L"[-NAN]", L"[NAN]", L"[+NAN]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%e]", L"[%+e]",
+ L"[-inf]", L"[inf]", L"[+inf]",
+ L"[-nan]", L"[nan]", L"[+nan]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%E]", L"[%+E]",
+ L"[-INF]", L"[INF]", L"[+INF]",
+ L"[-NAN]", L"[NAN]", L"[+NAN]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%f]", L"[%+f]",
+ L"[-inf]", L"[inf]", L"[+inf]",
+ L"[-nan]", L"[nan]", L"[+nan]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%F]", L"[%+F]",
+ L"[-INF]", L"[INF]", L"[+INF]",
+ L"[-NAN]", L"[NAN]", L"[+NAN]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%g]", L"[%+g]",
+ L"[-inf]", L"[inf]", L"[+inf]",
+ L"[-nan]", L"[nan]", L"[+nan]");
+ CheckInfNan(swprintf, swscanf, L"%s",
+ L"[%G]", L"[%+G]",
+ L"[-INF]", L"[INF]", L"[+INF]",
+ L"[-NAN]", L"[NAN]", L"[+NAN]");
}
TEST(STDIO_TEST, snprintf_d_INT_MAX) {
@@ -646,15 +730,34 @@
fclose(fp);
}
-TEST(STDIO_TEST, sscanf) {
- char s1[123];
- int i1;
- double d1;
- char s2[123];
- ASSERT_EQ(3, sscanf(" hello 123 1.23 ", "%s %i %lf %s", s1, &i1, &d1, s2));
- ASSERT_STREQ("hello", s1);
- ASSERT_EQ(123, i1);
- ASSERT_DOUBLE_EQ(1.23, d1);
+TEST(STDIO_TEST, sscanf_swscanf) {
+ struct stuff {
+ char s1[123];
+ int i1;
+ double d1;
+ float f1;
+ char s2[123];
+
+ void Check() {
+ ASSERT_STREQ("hello", s1);
+ ASSERT_EQ(123, i1);
+ ASSERT_DOUBLE_EQ(1.23, d1);
+ ASSERT_FLOAT_EQ(9.0f, f1);
+ ASSERT_STREQ("world", s2);
+ }
+ } s;
+
+ memset(&s, 0, sizeof(s));
+ ASSERT_EQ(5, sscanf(" hello 123 1.23 0x1.2p3 world",
+ "%s %i %lf %f %s",
+ s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+ s.Check();
+
+ memset(&s, 0, sizeof(s));
+ ASSERT_EQ(5, swscanf(L" hello 123 1.23 0x1.2p3 world",
+ L"%s %i %lf %f %s",
+ s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+ s.Check();
}
TEST(STDIO_TEST, cantwrite_EBADF) {
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 773230f..5b9442f 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -17,16 +17,18 @@
#include <gtest/gtest.h>
#include "BionicDeathTest.h"
+#include "math_data_test.h"
#include "TemporaryFile.h"
#include "utils.h"
#include <errno.h>
+#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
+#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
-#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -288,16 +290,65 @@
ASSERT_DOUBLE_EQ(1.23, atof("1.23"));
}
+template <typename T>
+static void CheckStrToFloat(T fn(const char* s, char** end)) {
+ FpUlpEq<0, T> pred;
+
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn("9.0", nullptr));
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn("0.9e1", nullptr));
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn("0x1.2p3", nullptr));
+
+ EXPECT_TRUE(isnan(fn("+nan", nullptr)));
+ EXPECT_TRUE(isnan(fn("nan", nullptr)));
+ EXPECT_TRUE(isnan(fn("-nan", nullptr)));
+
+ EXPECT_TRUE(isnan(fn("+nan(0xff)", nullptr)));
+ EXPECT_TRUE(isnan(fn("nan(0xff)", nullptr)));
+ EXPECT_TRUE(isnan(fn("-nan(0xff)", nullptr)));
+
+ char* p;
+ EXPECT_TRUE(isnan(fn("+nanny", &p)));
+ EXPECT_STREQ("ny", p);
+ EXPECT_TRUE(isnan(fn("nanny", &p)));
+ EXPECT_STREQ("ny", p);
+ EXPECT_TRUE(isnan(fn("-nanny", &p)));
+ EXPECT_STREQ("ny", p);
+
+ EXPECT_EQ(0, fn("muppet", &p));
+ EXPECT_STREQ("muppet", p);
+ EXPECT_EQ(0, fn(" muppet", &p));
+ EXPECT_STREQ(" muppet", p);
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+inf", nullptr));
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("inf", nullptr));
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-inf", nullptr));
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+infinity", nullptr));
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("infinity", nullptr));
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-infinity", nullptr));
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+infinitude", &p));
+ EXPECT_STREQ("initude", p);
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("infinitude", &p));
+ EXPECT_STREQ("initude", p);
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-infinitude", &p));
+ EXPECT_STREQ("initude", p);
+
+ // Check case-insensitivity.
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("InFiNiTy", nullptr));
+ EXPECT_TRUE(isnan(fn("NaN", nullptr)));
+}
+
TEST(stdlib, strtod) {
- ASSERT_DOUBLE_EQ(1.23, strtod("1.23", NULL));
+ CheckStrToFloat(strtod);
}
TEST(stdlib, strtof) {
- ASSERT_FLOAT_EQ(1.23, strtof("1.23", NULL));
+ CheckStrToFloat(strtof);
}
TEST(stdlib, strtold) {
- ASSERT_DOUBLE_EQ(1.23, strtold("1.23", NULL));
+ CheckStrToFloat(strtold);
}
TEST(stdlib, strtof_2206701) {
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 000dc98..db51c08 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -22,6 +22,8 @@
#include <stdint.h>
#include <wchar.h>
+#include "math_data_test.h"
+
#define NUM_WCHARS(num_bytes) ((num_bytes)/sizeof(wchar_t))
TEST(wchar, sizeof_wchar_t) {
@@ -389,14 +391,6 @@
ASSERT_EQ('\x20', *invalid);
}
-TEST(wchar, wcstod) {
- ASSERT_DOUBLE_EQ(1.23, wcstod(L"1.23", NULL));
-}
-
-TEST(wchar, wcstof) {
- ASSERT_FLOAT_EQ(1.23f, wcstof(L"1.23", NULL));
-}
-
TEST(wchar, wcstol) {
ASSERT_EQ(123L, wcstol(L"123", NULL, 0));
}
@@ -405,10 +399,6 @@
ASSERT_EQ(123LL, wcstol(L"123", NULL, 0));
}
-TEST(wchar, wcstold) {
- ASSERT_DOUBLE_EQ(1.23L, wcstold(L"1.23", NULL));
-}
-
TEST(wchar, wcstoul) {
ASSERT_EQ(123UL, wcstoul(L"123", NULL, 0));
}
@@ -672,3 +662,64 @@
wchar_t dst[6];
ASSERT_EQ(&dst[4], wmempcpy(dst, L"hello", 4));
}
+
+template <typename T>
+static void CheckWcsToFloat(T fn(const wchar_t* s, wchar_t** end)) {
+ FpUlpEq<0, T> pred;
+
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"9.0", nullptr));
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"0.9e1", nullptr));
+ EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"0x1.2p3", nullptr));
+
+ EXPECT_TRUE(isnan(fn(L"+nan", nullptr)));
+ EXPECT_TRUE(isnan(fn(L"nan", nullptr)));
+ EXPECT_TRUE(isnan(fn(L"-nan", nullptr)));
+
+ EXPECT_TRUE(isnan(fn(L"+nan(0xff)", nullptr)));
+ EXPECT_TRUE(isnan(fn(L"nan(0xff)", nullptr)));
+ EXPECT_TRUE(isnan(fn(L"-nan(0xff)", nullptr)));
+
+ wchar_t* p;
+ EXPECT_TRUE(isnan(fn(L"+nanny", &p)));
+ EXPECT_STREQ(L"ny", p);
+ EXPECT_TRUE(isnan(fn(L"nanny", &p)));
+ EXPECT_STREQ(L"ny", p);
+ EXPECT_TRUE(isnan(fn(L"-nanny", &p)));
+ EXPECT_STREQ(L"ny", p);
+
+ EXPECT_EQ(0, fn(L"muppet", &p));
+ EXPECT_STREQ(L"muppet", p);
+ EXPECT_EQ(0, fn(L" muppet", &p));
+ EXPECT_STREQ(L" muppet", p);
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+inf", nullptr));
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"inf", nullptr));
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-inf", nullptr));
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+infinity", nullptr));
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"infinity", nullptr));
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-infinity", nullptr));
+
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+infinitude", &p));
+ EXPECT_STREQ(L"initude", p);
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"infinitude", &p));
+ EXPECT_STREQ(L"initude", p);
+ EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-infinitude", &p));
+ EXPECT_STREQ(L"initude", p);
+
+ // Check case-insensitivity.
+ EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"InFiNiTy", nullptr));
+ EXPECT_TRUE(isnan(fn(L"NaN", nullptr)));
+}
+
+TEST(wchar, wcstod) {
+ CheckWcsToFloat(wcstod);
+}
+
+TEST(wchar, wcstof) {
+ CheckWcsToFloat(wcstof);
+}
+
+TEST(wchar, wcstold) {
+ CheckWcsToFloat(wcstold);
+}