// Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.
// This file tests string processing functions related to numeric values.
using absl::SimpleAtoi; using absl::SimpleHexAtoi; using absl::numbers_internal::kSixDigitsToBufferSize; using absl::numbers_internal::safe_strto32_base; using absl::numbers_internal::safe_strto64_base; using absl::numbers_internal::safe_strtou32_base; using absl::numbers_internal::safe_strtou64_base; using absl::numbers_internal::SixDigitsToBuffer; using absl::strings_internal::Itoa; using absl::strings_internal::strtouint32_test_cases; using absl::strings_internal::strtouint64_test_cases; using testing::Eq; using testing::MatchesRegex; using testing::Pointee;
// Number of floats to test with. // 5,000,000 is a reasonable default for a test that only takes a few seconds. // 1,000,000,000+ triggers checking for all possible mantissa values for // double-precision tests. 2,000,000,000+ triggers checking for every possible // single-precision float. constint kFloatNumCases = 5000000;
// This is a slow, brute-force routine to compute the exact base-10 // representation of a double-precision floating-point number. It // is useful for debugging only.
std::string PerfectDtoa(double d) { if (d == 0) return"0"; if (d < 0) return"-" + PerfectDtoa(-d);
// Basic theory: decompose d into mantissa and exp, where // d = mantissa * 2^exp, and exp is as close to zero as possible.
int64_t mantissa, exp = 0; while (d >= 1ULL << 63) ++exp, d *= 0.5; while ((mantissa = d) != d) --exp, d *= 2.0;
// Then convert mantissa to ASCII, and either double it (if // exp > 0) or halve it (if exp < 0) repeatedly. "halve it" // in this case means multiplying it by five and dividing by 10.
constexpr int maxlen = 1100; // worst case is actually 1030 or so. char buf[maxlen + 5]; for (int64_t num = mantissa, pos = maxlen; --pos >= 0;) {
buf[pos] = '0' + (num % 10);
num /= 10;
} char* begin = &buf[0]; char* end = buf + maxlen; for (int i = 0; i != exp; i += (exp > 0) ? 1 : -1) { int carry = 0; for (char* p = end; --p != begin;) { int dig = *p - '0';
dig = dig * (exp > 0 ? 2 : 5) + carry;
carry = dig / 10;
dig %= 10;
*p = '0' + dig;
}
} if (exp < 0) { // "dividing by 10" above means we have to add the decimal point.
memmove(end + 1 + exp, end + exp, 1 - exp);
end[exp] = '.';
++end;
} while (*begin == '0' && begin[1] != '.') ++begin; return {begin, end};
}
TEST(ToString, PerfectDtoa) {
EXPECT_THAT(PerfectDtoa(1), Eq("1"));
EXPECT_THAT(PerfectDtoa(0.1),
Eq("0.1000000000000000055511151231257827021181583404541015625"));
EXPECT_THAT(PerfectDtoa(1e24), Eq("999999999999999983222784"));
EXPECT_THAT(PerfectDtoa(5e-324), MatchesRegex("0.0000.*625")); for (int i = 0; i < 100; ++i) { for (double multiplier :
{1e-300, 1e-200, 1e-100, 0.1, 1.0, 10.0, 1e100, 1e300}) { double d = multiplier * i;
std::string s = PerfectDtoa(d);
EXPECT_DOUBLE_EQ(d, strtod(s.c_str(), nullptr));
}
}
}
TEST(Numbers, TestFastPrints) { for (int i = -100; i <= 100; i++) {
CheckInt32(i);
CheckInt64(i);
} for (int i = 0; i <= 100; i++) {
CheckUInt32(i);
CheckUInt64(i);
} // Test min int to make sure that works
CheckInt32(INT_MIN);
CheckInt32(INT_MAX);
CheckInt64(LONG_MIN);
CheckInt64(uint64_t{1000000000});
CheckInt64(uint64_t{9999999999});
CheckInt64(uint64_t{100000000000000});
CheckInt64(uint64_t{999999999999999});
CheckInt64(uint64_t{1000000000000000000});
CheckInt64(uint64_t{1199999999999999999});
CheckInt64(int64_t{-700000000000000000});
CheckInt64(LONG_MAX);
CheckUInt32(std::numeric_limits<uint32_t>::max());
CheckUInt64(uint64_t{1000000000});
CheckUInt64(uint64_t{9999999999});
CheckUInt64(uint64_t{100000000000000});
CheckUInt64(uint64_t{999999999999999});
CheckUInt64(uint64_t{1000000000000000000});
CheckUInt64(uint64_t{1199999999999999999});
CheckUInt64(std::numeric_limits<uint64_t>::max());
for (int i = 0; i < 10000; i++) {
CheckHex64(i);
}
CheckHex64(uint64_t{0x123456789abcdef0});
}
template <typename int_type, typename in_val_type> void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) {
std::string s; // (u)int128 can be streamed but not StrCat'd.
absl::strings_internal::OStringStream(&s) << in_value;
int_type x = static_cast<int_type>(~exp_value);
EXPECT_TRUE(SimpleAtoi(s, &x))
<< "in_value=" << in_value << " s=" << s << " x=" << x;
EXPECT_EQ(exp_value, x);
x = static_cast<int_type>(~exp_value);
EXPECT_TRUE(SimpleAtoi(s.c_str(), &x));
EXPECT_EQ(exp_value, x);
}
template <typename int_type, typename in_val_type> void VerifySimpleAtoiBad(in_val_type in_value) {
std::string s; // (u)int128 can be streamed but not StrCat'd.
absl::strings_internal::OStringStream(&s) << in_value;
int_type x;
EXPECT_FALSE(SimpleAtoi(s, &x));
EXPECT_FALSE(SimpleAtoi(s.c_str(), &x));
}
TEST(NumbersTest, Atod) { // DBL_TRUE_MIN and FLT_TRUE_MIN were not mandated in <cfloat> before C++17. #if !defined(DBL_TRUE_MIN) static constexpr double DBL_TRUE_MIN = 4.940656458412465441765687928682213723650598026143247644255856825e-324; #endif #if !defined(FLT_TRUE_MIN) static constexpr float FLT_TRUE_MIN = 1.401298464324817070923729583289916131280261941876515771757068284e-45f; #endif
double d; float f;
// NaN can be spelled in multiple ways.
EXPECT_TRUE(absl::SimpleAtod("NaN", &d));
EXPECT_TRUE(std::isnan(d));
EXPECT_TRUE(absl::SimpleAtod("nAN", &d));
EXPECT_TRUE(std::isnan(d));
EXPECT_TRUE(absl::SimpleAtod("-nan", &d));
EXPECT_TRUE(std::isnan(d));
// Parse DBL_MAX. Parsing something more than twice as big should also // produce infinity.
EXPECT_TRUE(absl::SimpleAtod("1.7976931348623157e+308", &d));
EXPECT_EQ(d, 1.7976931348623157e+308);
EXPECT_TRUE(absl::SimpleAtod("5e308", &d));
EXPECT_TRUE(std::isinf(d) && (d > 0)); // Ditto, but for FLT_MAX.
EXPECT_TRUE(absl::SimpleAtof("3.4028234663852886e+38", &f));
EXPECT_EQ(f, 3.4028234663852886e+38f);
EXPECT_TRUE(absl::SimpleAtof("7e38", &f));
EXPECT_TRUE(std::isinf(f) && (f > 0));
// Parse the largest N such that parsing 1eN produces a finite value and the // smallest M = N + 1 such that parsing 1eM produces infinity. // // The 309 exponent (and 39) confirms the "definition of // kEiselLemireMaxExclExp10" comment in charconv.cc.
EXPECT_TRUE(absl::SimpleAtod("1e308", &d));
EXPECT_EQ(d, 1e308);
EXPECT_FALSE(std::isinf(d));
EXPECT_TRUE(absl::SimpleAtod("1e309", &d));
EXPECT_TRUE(std::isinf(d)); // Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("1e38", &f));
EXPECT_EQ(f, 1e38f);
EXPECT_FALSE(std::isinf(f));
EXPECT_TRUE(absl::SimpleAtof("1e39", &f));
EXPECT_TRUE(std::isinf(f));
// Parse the largest N such that parsing 9.999999999999999999eN, with 19 // nines, produces a finite value. // // 9999999999999999999, with 19 nines but no decimal point, is the largest // "repeated nines" integer that fits in a uint64_t.
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e307", &d));
EXPECT_EQ(d, 9.999999999999999999e307);
EXPECT_FALSE(std::isinf(d));
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e308", &d));
EXPECT_TRUE(std::isinf(d)); // Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e37", &f));
EXPECT_EQ(f, 9.999999999999999999e37f);
EXPECT_FALSE(std::isinf(f));
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e38", &f));
EXPECT_TRUE(std::isinf(f));
// Parse the largest N (the most negative -N) such that parsing 1e-N produces // a normal or subnormal (but still positive) or zero value.
EXPECT_TRUE(absl::SimpleAtod("1e-307", &d));
EXPECT_EQ(d, 1e-307);
EXPECT_GE(d, DBL_MIN);
EXPECT_LT(d, DBL_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("1e-323", &d));
EXPECT_EQ(d, 1e-323);
EXPECT_GE(d, DBL_TRUE_MIN);
EXPECT_LT(d, DBL_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("1e-324", &d));
EXPECT_EQ(d, 0); // Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("1e-37", &f));
EXPECT_EQ(f, 1e-37f);
EXPECT_GE(f, FLT_MIN);
EXPECT_LT(f, FLT_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("1e-45", &f));
EXPECT_EQ(f, 1e-45f);
EXPECT_GE(f, FLT_TRUE_MIN);
EXPECT_LT(f, FLT_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("1e-46", &f));
EXPECT_EQ(f, 0);
// Parse the largest N (the most negative -N) such that parsing // 9.999999999999999999e-N, with 19 nines, produces a normal or subnormal // (but still positive) or zero value. // // 9999999999999999999, with 19 nines but no decimal point, is the largest // "repeated nines" integer that fits in a uint64_t. // // The -324/-325 exponents (and -46/-47) confirms the "definition of // kEiselLemireMinInclExp10" comment in charconv.cc.
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-308", &d));
EXPECT_EQ(d, 9.999999999999999999e-308);
EXPECT_GE(d, DBL_MIN);
EXPECT_LT(d, DBL_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-324", &d));
EXPECT_EQ(d, 9.999999999999999999e-324);
EXPECT_GE(d, DBL_TRUE_MIN);
EXPECT_LT(d, DBL_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-325", &d));
EXPECT_EQ(d, 0); // Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-38", &f));
EXPECT_EQ(f, 9.999999999999999999e-38f);
EXPECT_GE(f, FLT_MIN);
EXPECT_LT(f, FLT_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-46", &f));
EXPECT_EQ(f, 9.999999999999999999e-46f);
EXPECT_GE(f, FLT_TRUE_MIN);
EXPECT_LT(f, FLT_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-47", &f));
EXPECT_EQ(f, 0);
// Leading and/or trailing whitespace is OK.
EXPECT_TRUE(absl::SimpleAtod(" \t\r\n 2.718", &d));
EXPECT_EQ(d, 2.718);
EXPECT_TRUE(absl::SimpleAtod(" 3.141 ", &d));
EXPECT_EQ(d, 3.141);
// Leading or trailing not-whitespace is not OK.
EXPECT_FALSE(absl::SimpleAtod("n 0", &d));
EXPECT_FALSE(absl::SimpleAtod("0n ", &d));
// Multiple leading 0s are OK.
EXPECT_TRUE(absl::SimpleAtod("000123", &d));
EXPECT_EQ(d, 123);
EXPECT_TRUE(absl::SimpleAtod("000.456", &d));
EXPECT_EQ(d, 0.456);
// An absent leading 0 (for a fraction < 1) is OK.
EXPECT_TRUE(absl::SimpleAtod(".5", &d));
EXPECT_EQ(d, 0.5);
EXPECT_TRUE(absl::SimpleAtod("-.707", &d));
EXPECT_EQ(d, -0.707);
// Unary + is OK.
EXPECT_TRUE(absl::SimpleAtod("+6.0221408e+23", &d));
EXPECT_EQ(d, 6.0221408e+23);
// Underscores are not OK.
EXPECT_FALSE(absl::SimpleAtod("123_456", &d));
// The decimal separator must be '.' and is never ','.
EXPECT_TRUE(absl::SimpleAtod("8.9", &d));
EXPECT_FALSE(absl::SimpleAtod("8,9", &d));
// These examples are called out in the EiselLemire function's comments.
EXPECT_TRUE(absl::SimpleAtod("4503599627370497.5", &d));
EXPECT_EQ(d, 4503599627370497.5);
EXPECT_TRUE(absl::SimpleAtod("1e+23", &d));
EXPECT_EQ(d, 1e+23);
EXPECT_TRUE(absl::SimpleAtod("9223372036854775807", &d));
EXPECT_EQ(d, 9223372036854775807); // Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("0.0625", &f));
EXPECT_EQ(f, 0.0625f);
EXPECT_TRUE(absl::SimpleAtof("20040229.0", &f));
EXPECT_EQ(f, 20040229.0f);
EXPECT_TRUE(absl::SimpleAtof("2147483647.0", &f));
EXPECT_EQ(f, 2147483647.0f);
// Some parsing algorithms don't always round correctly (but absl::SimpleAtod // should). This test case comes from // https://github.com/serde-rs/json/issues/707 // // See also atod_manual_test.cc for running many more test cases.
EXPECT_TRUE(absl::SimpleAtod("122.416294033786585", &d));
EXPECT_EQ(d, 122.416294033786585);
EXPECT_TRUE(absl::SimpleAtof("122.416294033786585", &f));
EXPECT_EQ(f, 122.416294033786585f);
}
TEST(stringtest, safe_strto32_leading_substring) { // These tests verify this comment in numbers.h: // On error, returns false, and sets *value to: [...] // conversion of leading substring if available ("123@@@" -> 123) // 0 if no leading substring available
int32_t value;
EXPECT_FALSE(safe_strto32_base("04069@@@", &value, 10));
EXPECT_EQ(4069, value);
EXPECT_FALSE(safe_strto32_base("@@@", &value, 10));
EXPECT_EQ(0, value); // there was no leading substring
}
TEST(stringtest, safe_strto64_leading_substring) { // These tests verify this comment in numbers.h: // On error, returns false, and sets *value to: [...] // conversion of leading substring if available ("123@@@" -> 123) // 0 if no leading substring available
int64_t value;
EXPECT_FALSE(safe_strto64_base("04069@@@", &value, 10));
EXPECT_EQ(4069, value);
// Out of bounds.
EXPECT_FALSE(safe_strto64_base("9223372036854775808", &value, 10));
EXPECT_FALSE(safe_strto64_base("-9223372036854775809", &value, 10));
TEST(stringtest, safe_strto32_random) {
test_random_integer_parse_base<int32_t>(&safe_strto32_base);
}
TEST(stringtest, safe_strto64_random) {
test_random_integer_parse_base<int64_t>(&safe_strto64_base);
}
TEST(stringtest, safe_strtou32_random) {
test_random_integer_parse_base<uint32_t>(&safe_strtou32_base);
}
TEST(stringtest, safe_strtou64_random) {
test_random_integer_parse_base<uint64_t>(&safe_strtou64_base);
}
TEST(stringtest, safe_strtou128_random) { // random number generators don't work for uint128, and // uint128 can be streamed but not StrCat'd, so this code must be custom // implemented for uint128, but is generally the same as what's above. // test_random_integer_parse_base<absl::uint128>( // &absl::numbers_internal::safe_strtou128_base); using RandomEngine = std::minstd_rand0; using IntType = absl::uint128;
constexpr auto parse_func = &absl::numbers_internal::safe_strtou128_base;
for (size_t i = 0; i < kNumRandomTests; i++) {
IntType value = random_uint64(rng);
value = (value << 64) + random_uint64(rng); int base = random_base(rng);
std::string str_value;
EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
IntType parsed_value;
// Test successful parse
EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
EXPECT_EQ(parsed_value, value);
// Test underflow
s.clear();
absl::strings_internal::OStringStream(&s) << "-" << value;
EXPECT_FALSE(parse_func(s, &parsed_value, base));
}
}
TEST(stringtest, safe_strto128_random) { // random number generators don't work for int128, and // int128 can be streamed but not StrCat'd, so this code must be custom // implemented for int128, but is generally the same as what's above. // test_random_integer_parse_base<absl::int128>( // &absl::numbers_internal::safe_strto128_base); using RandomEngine = std::minstd_rand0; using IntType = absl::int128;
constexpr auto parse_func = &absl::numbers_internal::safe_strto128_base;
for (size_t i = 0; i < kNumRandomTests; ++i) {
int64_t high = random_int64(rng);
uint64_t low = random_uint64(rng);
IntType value = absl::MakeInt128(high, low);
int base = random_base(rng);
std::string str_value;
EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
IntType parsed_value;
// Test successful parse
EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
EXPECT_EQ(parsed_value, value);
// feenableexcept() and fedisableexcept() are extensions supported by some libc // implementations. #ifdefined(__GLIBC__) || defined(__BIONIC__) #define ABSL_HAVE_FEENABLEEXCEPT 1 #define ABSL_HAVE_FEDISABLEEXCEPT 1 #endif
class SimpleDtoaTest : public testing::Test { protected: void SetUp() override { // Store the current floating point env & clear away any pending exceptions.
feholdexcept(&fp_env_); #ifdef ABSL_HAVE_FEENABLEEXCEPT // Turn on floating point exceptions.
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif
}
void TearDown() override { // Restore the floating point environment to the original state. // In theory fedisableexcept is unnecessary; fesetenv will also do it. // In practice, our toolchains have subtle bugs. #ifdef ABSL_HAVE_FEDISABLEEXCEPT
fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif
fesetenv(&fp_env_);
}
std::string ToNineDigits(double value) { char buffer[16]; // more than enough for %.9g
snprintf(buffer, sizeof(buffer), "%.9g", value); return buffer;
}
fenv_t fp_env_;
};
// Run the given runnable functor for "cases" test cases, chosen over the // available range of float. pi and e and 1/e are seeded, and then all // available integer powers of 2 and 10 are multiplied against them. In // addition to trying all those values, we try the next higher and next lower // float, and then we add additional test cases evenly distributed between them. // Each test case is passed to runnable as both a positive and negative value. template <typename R> void ExhaustiveFloat(uint32_t cases, R&& runnable) {
runnable(0.0f);
runnable(-0.0f); if (cases >= 2e9) { // more than 2 billion? Might as well run them all. for (float f = 0; f < std::numeric_limits<float>::max(); ) {
f = nextafterf(f, std::numeric_limits<float>::max());
runnable(-f);
runnable(f);
} return;
}
std::set<float> floats = {3.4028234e38f}; for (float f : {1.0, 3.14159265, 2.718281828, 1 / 2.718281828}) { for (float testf = f; testf != 0; testf *= 0.1f) floats.insert(testf); for (float testf = f; testf != 0; testf *= 0.5f) floats.insert(testf); for (float testf = f; testf < 3e38f / 2; testf *= 2.0f)
floats.insert(testf); for (float testf = f; testf < 3e38f / 10; testf *= 10) floats.insert(testf);
}
float last = *floats.begin();
runnable(last);
runnable(-last); int iters_per_float = cases / floats.size(); if (iters_per_float == 0) iters_per_float = 1; for (float f : floats) { if (f == last) continue; float testf = std::nextafter(last, std::numeric_limits<float>::max());
runnable(testf);
runnable(-testf);
last = testf; if (f == last) continue; double step = (double{f} - last) / iters_per_float; for (double d = last + step; d < f; d += step) {
testf = d; if (testf != last) {
runnable(testf);
runnable(-testf);
last = testf;
}
}
testf = std::nextafter(f, 0.0f); if (testf > last) {
runnable(testf);
runnable(-testf);
last = testf;
} if (f != last) {
runnable(f);
runnable(-f);
last = f;
}
}
}
TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) {
uint64_t test_count = 0;
std::vector<double> mismatches; auto checker = [&](double d) { if (d != d) return; // rule out NaNs
++test_count; char sixdigitsbuf[kSixDigitsToBufferSize] = {0};
SixDigitsToBuffer(d, sixdigitsbuf); char snprintfbuf[kSixDigitsToBufferSize] = {0};
snprintf(snprintfbuf, kSixDigitsToBufferSize, "%g", d); if (strcmp(sixdigitsbuf, snprintfbuf) != 0) {
mismatches.push_back(d); if (mismatches.size() < 10) {
LOG(ERROR) << "Six-digit failure with double. d=" << d
<< " sixdigits=" << sixdigitsbuf
<< " printf(%g)=" << snprintfbuf;
}
}
}; // Some quick sanity checks...
checker(5e-324);
checker(1e-308);
checker(1.0);
checker(1.000005);
checker(1.7976931348623157e308);
checker(0.00390625); #ifndef _MSC_VER // on MSVC, snprintf() rounds it to 0.00195313. SixDigitsToBuffer() rounds it // to 0.00195312 (round half to even).
checker(0.001953125); #endif
checker(0.005859375); // Some cases where the rounding is very very close
checker(1.089095e-15);
checker(3.274195e-55);
checker(6.534355e-146);
checker(2.920845e+234);
if (mismatches.empty()) {
test_count = 0;
ExhaustiveFloat(kFloatNumCases, checker);
test_count = 0;
std::vector<int> digit_testcases{ 100000, 100001, 100002, 100005, 100010, 100020, 100050, 100100, // misc 195312, 195313, // 1.953125 is a case where we round down, just barely. 200000, 500000, 800000, // misc mid-range cases 585937, 585938, // 5.859375 is a case where we round up, just barely. 900000, 990000, 999000, 999900, 999990, 999996, 999997, 999998, 999999}; if (kFloatNumCases >= 1e9) { // If at least 1 billion test cases were requested, user wants an // exhaustive test. So let's test all mantissas, too.
constexpr int min_mantissa = 100000, max_mantissa = 999999;
digit_testcases.resize(max_mantissa - min_mantissa + 1);
std::iota(digit_testcases.begin(), digit_testcases.end(), min_mantissa);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.