/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you 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 http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include CPPUNIT_NS_BEGIN template<> struct assertion_traits { static bool equal( const rtl_math_ConversionStatus& x, const rtl_math_ConversionStatus& y ) { return x == y; } static std::string toString( const rtl_math_ConversionStatus& x ) { OStringStream ost; ost << static_cast(x); return ost.str(); } }; CPPUNIT_NS_END namespace { class Test: public CppUnit::TestFixture { public: void test_stringToDouble_good() { rtl_math_ConversionStatus status; sal_Int32 end; double res = rtl::math::stringToDouble( OUString(" +1.E01foo"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(RTL_CONSTASCII_LENGTH(" +1.E01"), end); CPPUNIT_ASSERT_EQUAL(10.0, res); res = rtl::math::stringToDouble( OUString("NaN"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), end); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isNan(res)); res = rtl::math::stringToDouble( OUString("NaN1.23"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), end); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isNan(res)); res = rtl::math::stringToDouble( OUString("INF"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_OutOfRange, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), end); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isInf(res)); res = rtl::math::stringToDouble( OUString("INF1.23"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_OutOfRange, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), end); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isInf(res)); res = rtl::math::stringToDouble( OUString(".5"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), end); CPPUNIT_ASSERT_EQUAL(0.5, res); res = rtl::math::stringToDouble( OUString("5."), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), end); CPPUNIT_ASSERT_EQUAL(5.0, res); // Leading 0 and group separator. res = rtl::math::stringToDouble( OUString("0,123"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(5), end); CPPUNIT_ASSERT_EQUAL(123.0, res); // Leading 0 and two consecutive group separators are none. res = rtl::math::stringToDouble( OUString("0,,1"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), end); CPPUNIT_ASSERT_EQUAL(0.0, res); // Leading 0 and group separator at end is none. res = rtl::math::stringToDouble( OUString("0,"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), end); CPPUNIT_ASSERT_EQUAL(0.0, res); // Leading 0 and group separator before non-digit is none. res = rtl::math::stringToDouble( OUString("0,x"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), end); CPPUNIT_ASSERT_EQUAL(0.0, res); // Trailing group separator is none. res = rtl::math::stringToDouble( OUString("1,234,"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(5), end); CPPUNIT_ASSERT_EQUAL(1234.0, res); // Group separator followed by non-digit is none. res = rtl::math::stringToDouble( OUString("1,234,x"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(5), end); CPPUNIT_ASSERT_EQUAL(1234.0, res); } void test_stringToDouble_bad() { rtl_math_ConversionStatus status; sal_Int32 end; double res = rtl::math::stringToDouble( OUString(" +Efoo"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), end); CPPUNIT_ASSERT_EQUAL(0.0, res); res = rtl::math::stringToDouble( OUString("."), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), end); CPPUNIT_ASSERT_EQUAL(0.0, res); res = rtl::math::stringToDouble( OUString(" +.Efoo"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), end); CPPUNIT_ASSERT_EQUAL(0.0, res); res = rtl::math::stringToDouble( OUString(" +,.Efoo"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), end); CPPUNIT_ASSERT_EQUAL(0.0, res); // Leading group separator is none. res = rtl::math::stringToDouble( OUString(",1234"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), end); CPPUNIT_ASSERT_EQUAL(0.0, res); } void test_stringToDouble_exponent_without_digit() { rtl_math_ConversionStatus status; sal_Int32 end; double res = rtl::math::stringToDouble( OUString("1e"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(RTL_CONSTASCII_LENGTH("1"), end); CPPUNIT_ASSERT_EQUAL(1.0, res); res = rtl::math::stringToDouble( OUString("0e"), '.', ',', &status, &end); CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(RTL_CONSTASCII_LENGTH("1"), end); CPPUNIT_ASSERT_EQUAL(0.0, res); } void test_doubleToString() { double fVal = 999999999999999.0; sal_Int32 aGroups[3] = { 3, 2, 0 }; OUString aRes( rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', aGroups, ',', true)); CPPUNIT_ASSERT_EQUAL( OUString("99,99,99,99,99,99,999"), aRes); fVal = 949.0; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, -2, // round before decimals '.', aGroups, ',', true); CPPUNIT_ASSERT_EQUAL( OUString("900"), aRes); fVal = 950.0; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, -2, // round before decimals '.', aGroups, ',', true); CPPUNIT_ASSERT_EQUAL( OUString("1,000"), aRes); fVal = 4503599627370495.0; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.'); CPPUNIT_ASSERT_EQUAL( OUString("4503599627370495"), aRes); fVal = 4503599627370496.0; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, 2, '.'); CPPUNIT_ASSERT_EQUAL( OUString("4503599627370496.00"), aRes); fVal = 9007199254740991.0; // (2^53)-1 aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("9007199254740991"), aRes); fVal = 9007199254740992.0; // (2^53), algorithm switch aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("9.00719925474099E+015"), aRes); fVal = 9007199254740993.0; // (2^53)+1 would be but is 9007199254740992 aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("9.00719925474099E+015"), aRes); // Test rtl_math_StringFormat_G fVal = 0.001234567; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("0.00123"), aRes); fVal = 123.4567; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("123"), aRes); fVal = 123.4567; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 4, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("123.5"), aRes); fVal = 99.6; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("99.6"), aRes); // Expected could be 1E+03 (as 999.6 rounded to 3 significant digits // results in 1000 with an exponent equal to significant digits). // Currently we don't and output 1000 instead, negligible. fVal = 999.6; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("1000"), aRes); fVal = 9999.6; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("1E+004"), aRes); fVal = 12345.6789; aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, -3, '.', true); CPPUNIT_ASSERT_EQUAL( OUString("1.2E+004"), aRes); } void test_approx() { // (2^53)-1 , (2^53)-3 CPPUNIT_ASSERT_EQUAL( false, rtl::math::approxEqual( 9007199254740991.0, 9007199254740989.0)); // (2^53)-1 , (2^53)-2 CPPUNIT_ASSERT_EQUAL( false, rtl::math::approxEqual( 9007199254740991.0, 9007199254740990.0)); // Note: the following are internally represented as 900719925474099.12 // and 900719925474098.88 and the difference is 0.25 ... CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 900719925474099.1, 900719925474098.9)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 72.944444444444443, 72.9444444444444)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 359650.27322404372, 359650.27322404401)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 5.3590326375710063e+238, 5.3590326375710109e+238)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 7.4124095894894475e+158, 7.4124095894894514e+158)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 1.2905754687023132e+79, 1.2905754687023098e+79)); CPPUNIT_ASSERT_EQUAL( true, rtl::math::approxEqual( 3.5612905090455637e+38, 3.5612905090455599e+38)); // 0.3 - 0.2 - 0.1 == 0.0 CPPUNIT_ASSERT_EQUAL( 0.0, rtl::math::approxSub( rtl::math::approxSub( 0.3, 0.2), 0.1)); // ((2^53)-1) - ((2^53)-2) == 1.0 CPPUNIT_ASSERT_EQUAL( 1.0, rtl::math::approxSub( 9007199254740991.0, 9007199254740990.0)); // (3^31) - ((3^31)-1) == 1.0 CPPUNIT_ASSERT_EQUAL( 1.0, rtl::math::approxSub( 617673396283947.0, 617673396283946.0)); } void test_erf() { double x, res; x = 0.0; res = rtl::math::erf(x); CPPUNIT_ASSERT_EQUAL(0.0,res); rtl::math::setInf( &x, false); res = rtl::math::erf(x); CPPUNIT_ASSERT_EQUAL(1.0,res); rtl::math::setInf( &x, true); res = rtl::math::erf(x); CPPUNIT_ASSERT_EQUAL(-1.0,res); rtl::math::setNan( &x); res = rtl::math::erf(x); CPPUNIT_ASSERT_EQUAL(true,rtl::math::isNan(res)); x = 3.0; res = rtl::math::erf(-x); CPPUNIT_ASSERT_DOUBLES_EQUAL( -rtl::math::erf(x), res, 1E-12); } void test_erfc() { double x, res; x = 0.0; res = rtl::math::erfc(x); CPPUNIT_ASSERT_EQUAL(1.0,res); rtl::math::setInf( &x, false); res = rtl::math::erfc(x); CPPUNIT_ASSERT_EQUAL(0.0,res); rtl::math::setInf( &x, true); res = rtl::math::erfc(x); CPPUNIT_ASSERT_EQUAL(2.0,res); rtl::math::setNan( &x); res = rtl::math::erfc(x); CPPUNIT_ASSERT_EQUAL(true,rtl::math::isNan(res)); x = 3.0; res = rtl::math::erfc(-x); CPPUNIT_ASSERT_DOUBLES_EQUAL( 2.0 - rtl::math::erfc(x), res, 1E-12); } void test_expm1() { double x, res; x = 0.0; res = rtl::math::expm1(x); CPPUNIT_ASSERT_EQUAL(0.0,res); x = -0.0; res = rtl::math::expm1(x); CPPUNIT_ASSERT_EQUAL(-0.0,res); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isSignBitSet(res)); rtl::math::setInf( &x, false); res = rtl::math::expm1(x); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isInf(res) && !rtl::math::isSignBitSet(res)); rtl::math::setInf( &x, true); res = rtl::math::expm1(x); CPPUNIT_ASSERT_EQUAL(-1.0,res); rtl::math::setNan( &x); res = rtl::math::expm1(x); CPPUNIT_ASSERT_EQUAL(true,rtl::math::isNan(res)); } void test_log1p() { double x, res; x = 0.0; res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(0.0,res); x = -0.0; res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(-0.0,res); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isSignBitSet(res)); rtl::math::setInf( &x, false); res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isInf(res) && !rtl::math::isSignBitSet(res)); x = -1.0; res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isInf(res) && rtl::math::isSignBitSet(res)); x = -1.1; res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isNan(res)); rtl::math::setInf( &x, true); res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(true, rtl::math::isNan(res)); rtl::math::setNan( &x); res = rtl::math::log1p(x); CPPUNIT_ASSERT_EQUAL(true,rtl::math::isNan(res)); } void test_acosh() { double res; res = rtl::math::acosh(-1.0); // NaN CPPUNIT_ASSERT(rtl::math::isNan(res)); res = rtl::math::acosh(0.0); // NaN CPPUNIT_ASSERT(rtl::math::isNan(res)); res = rtl::math::acosh(0.5); // NaN CPPUNIT_ASSERT(rtl::math::isNan(res)); CPPUNIT_ASSERT_EQUAL(0.0, rtl::math::acosh(1.0)); res = rtl::math::acosh(std::numeric_limits::infinity()); // +Inf CPPUNIT_ASSERT(!rtl::math::isSignBitSet(res)); CPPUNIT_ASSERT(rtl::math::isInf(res)); // #i97605 CPPUNIT_ASSERT_DOUBLES_EQUAL(692.56728736744176, rtl::math::acosh(3e+300), 1e-15); CPPUNIT_ASSERT_DOUBLES_EQUAL(0.014142017775252324, rtl::math::acosh(1.0001), 1e-15); } void test_asinh() { double res; res = rtl::math::asinh(-std::numeric_limits::infinity()); // -Inf CPPUNIT_ASSERT(rtl::math::isSignBitSet(res)); CPPUNIT_ASSERT(rtl::math::isInf(res)); CPPUNIT_ASSERT_EQUAL(0.0, rtl::math::asinh(0.0)); res = rtl::math::asinh(std::numeric_limits::infinity()); // +Inf CPPUNIT_ASSERT(!rtl::math::isSignBitSet(res)); CPPUNIT_ASSERT(rtl::math::isInf(res)); // #i97605 CPPUNIT_ASSERT_DOUBLES_EQUAL(691.67568924815798, rtl::math::asinh(1.23e+300), 1e-15); CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0350378961923076, rtl::math::asinh(1.23), 1e-16); CPPUNIT_ASSERT_DOUBLES_EQUAL(1.23e-300, rtl::math::asinh(1.23e-300), 1e-303); // asinh is an odd function CPPUNIT_ASSERT_EQUAL(-rtl::math::asinh(1.23e+300), rtl::math::asinh(-1.23e+300)); CPPUNIT_ASSERT_EQUAL(-rtl::math::asinh(1.23), rtl::math::asinh(-1.23)); CPPUNIT_ASSERT_EQUAL(-rtl::math::asinh(1.23e-300), rtl::math::asinh(-1.23e-300)); } void test_atanh() { double res; res = rtl::math::atanh(-2.0); // NaN CPPUNIT_ASSERT(rtl::math::isNan(res)); res = rtl::math::atanh(-1.0); // -Inf CPPUNIT_ASSERT(rtl::math::isSignBitSet(res)); CPPUNIT_ASSERT(rtl::math::isInf(res)); CPPUNIT_ASSERT_EQUAL(0.0, rtl::math::atanh(0.0)); res = rtl::math::atanh(1.0); // +Inf CPPUNIT_ASSERT(!rtl::math::isSignBitSet(res)); CPPUNIT_ASSERT(rtl::math::isInf(res)); res = rtl::math::atanh(2.0); // NaN CPPUNIT_ASSERT(rtl::math::isNan(res)); } CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(test_stringToDouble_good); CPPUNIT_TEST(test_stringToDouble_bad); CPPUNIT_TEST(test_stringToDouble_exponent_without_digit); CPPUNIT_TEST(test_doubleToString); CPPUNIT_TEST(test_erf); CPPUNIT_TEST(test_erfc); CPPUNIT_TEST(test_expm1); CPPUNIT_TEST(test_log1p); CPPUNIT_TEST(test_approx); CPPUNIT_TEST(test_acosh); CPPUNIT_TEST(test_asinh); CPPUNIT_TEST(test_atanh); CPPUNIT_TEST_SUITE_END(); }; CPPUNIT_TEST_SUITE_REGISTRATION(Test); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */