Edit

kc3-lang/angle/src/tests/compiler_tests/FloatLex_test.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2016-11-07 12:44:29
    Hash : 99bd5f40
    Message : Fix GLSL float parsing corner cases This fixes parsing floats that are out-of-range, and floats that have more digits than the standard library float parsing functions can handle. In these cases, we now fall back to a custom implementation of float parsing. The custom parsing path can correctly process floats with up to hundreds of millions of digits in their mantissa part. Rounding behavior of the custom float parser may not be entirely consistent with the standard parser, but the error should be at most a few ULP. This can be considered acceptable since floating point operations are not expected to be exact in GLSL in general. Settling for lower accuracy also enables the parser to run in constant memory, instead of having to store all the significant digits of the decimal mantissa being parsed. BUG=angleproject:1613 TEST=angle_unittests Change-Id: I04a5d9ae5aaca48ef14b79cca5b997078614eb1c Reviewed-on: https://chromium-review.googlesource.com/412082 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/tests/compiler_tests/FloatLex_test.cpp
  • //
    // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // FloatLex_test.cpp:
    // Tests for parsing floats in GLSL source.
    //
    
    #include <sstream>
    #include <string>
    
    #include "common/debug.h"
    #include "common/mathutil.h"
    #include "compiler/translator/util.h"
    #include "gtest/gtest.h"
    
    namespace
    {
    
    class StrtofClampParser
    {
      public:
        static float Parse(std::string str)
        {
            float value;
            sh::strtof_clamp(str, &value);
            return value;
        }
    };
    
    // NumericLexFloat32OutOfRangeToInfinity usually only comes to play in corner cases of parsing, but
    // it's useful to test that it works as expected across the whole range of floats.
    class NumericLexFloatParser
    {
      public:
        static float Parse(std::string str) { return sh::NumericLexFloat32OutOfRangeToInfinity(str); }
    };
    
    }  // anonymous namespace
    
    template <typename T>
    class FloatLexTest : public ::testing::Test
    {
      public:
        FloatLexTest() {}
    
      protected:
        void SetUp() override {}
    
        void TearDown() override {}
    
        static bool ParsedMatches(std::string str, float expected)
        {
            return (T::Parse(str) == expected);
        }
    
        static bool IsInfinity(std::string str)
        {
            float f = T::Parse(str);
            return gl::isInf(f);
        }
    
        static std::string Zeros(size_t count) { return std::string(count, '0'); }
    };
    
    typedef ::testing::Types<StrtofClampParser, NumericLexFloatParser> FloatParserTypes;
    TYPED_TEST_CASE(FloatLexTest, FloatParserTypes);
    
    TYPED_TEST(FloatLexTest, One)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("1.0", 1.0f));
    }
    
    TYPED_TEST(FloatLexTest, Ten)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("10.0", 10.0f));
    }
    
    TYPED_TEST(FloatLexTest, TenScientific)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("1.0e1", 10.0f));
    }
    
    TYPED_TEST(FloatLexTest, ScientificWithSmallMantissa)
    {
        std::stringstream ss;
        ss << "0." << TestFixture::Zeros(100) << "125e102";
        ASSERT_TRUE(TestFixture::ParsedMatches(ss.str(), 12.5f));
    }
    
    TYPED_TEST(FloatLexTest, ScientificWithLargeMantissa)
    {
        std::stringstream ss;
        ss << "9" << TestFixture::Zeros(100) << ".0e-100";
        ASSERT_TRUE(TestFixture::ParsedMatches(ss.str(), 9.0f));
    }
    
    TYPED_TEST(FloatLexTest, ScientificWithVerySmallMantissa)
    {
        std::stringstream ss;
        ss << "0." << TestFixture::Zeros(5000) << "125e5002";
        ASSERT_TRUE(TestFixture::ParsedMatches(ss.str(), 12.5f));
    }
    
    TYPED_TEST(FloatLexTest, ScientificWithVeryLargeMantissa)
    {
        std::stringstream ss;
        ss << "9" << TestFixture::Zeros(5000) << ".0e-5000";
        ASSERT_TRUE(TestFixture::ParsedMatches(ss.str(), 9.0f));
    }
    
    TYPED_TEST(FloatLexTest, StartWithDecimalDot)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches(".125", 0.125f));
    }
    
    TYPED_TEST(FloatLexTest, EndWithDecimalDot)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("123.", 123.0f));
    }
    
    TYPED_TEST(FloatLexTest, NoDecimalDot)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("125e-2", 1.25f));
    }
    
    TYPED_TEST(FloatLexTest, EndStartWithDecimalDotScientific)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches(".625e-1", 0.0625f));
    }
    
    TYPED_TEST(FloatLexTest, EndWithDecimalDotScientific)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("102400.e-2", 1024.0f));
    }
    
    TYPED_TEST(FloatLexTest, UppercaseE)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("125E-2", 1.25f));
    }
    
    TYPED_TEST(FloatLexTest, PlusInExponent)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("1E+2", 100.0f));
    }
    
    TYPED_TEST(FloatLexTest, SlightlyAboveMaxFloat)
    {
        ASSERT_TRUE(TestFixture::IsInfinity("3.4029e38"));
    }
    
    TYPED_TEST(FloatLexTest, SlightlyBelowMaxFloat)
    {
        ASSERT_FALSE(TestFixture::IsInfinity("3.4028e38"));
    }
    
    TYPED_TEST(FloatLexTest, SlightlyBelowMinSubnormalFloat)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("1.0e-48", 0.0f));
    }
    
    TYPED_TEST(FloatLexTest, SlightlyAboveMinNormalFloat)
    {
        ASSERT_FALSE(TestFixture::ParsedMatches("1.0e-37", 0.0f));
    }
    
    TYPED_TEST(FloatLexTest, ManySignificantDigits)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("1.23456789", 1.23456789f));
    }
    
    TYPED_TEST(FloatLexTest, MantissaBitAboveMaxUint)
    {
        ASSERT_TRUE(TestFixture::ParsedMatches("4294967299.", 4294967299.0f));
    }
    
    TYPED_TEST(FloatLexTest, ExponentBitAboveMaxInt)
    {
        ASSERT_TRUE(TestFixture::IsInfinity("1.0e2147483649"));
    }
    
    TYPED_TEST(FloatLexTest, ExponentBitBelowMaxIntAndLargeMantissa)
    {
        std::stringstream ss;
        ss << "1" << TestFixture::Zeros(32) << ".0e2147483640";
        ASSERT_TRUE(TestFixture::IsInfinity(ss.str()));
    }
    
    TYPED_TEST(FloatLexTest, ExponentBitAboveMinIntAndSmallMantissa)
    {
        std::stringstream ss;
        ss << "0." << TestFixture::Zeros(32) << "1e-2147483640";
        ASSERT_TRUE(TestFixture::ParsedMatches(ss.str(), 0.0f));
    }