[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 5235970 5/5: Prefer C++11 to C99 transcendent
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 5235970 5/5: Prefer C++11 to C99 transcendentals |
Date: |
Thu, 27 Apr 2017 11:54:07 -0400 (EDT) |
branch: master
commit 5235970fa4c406dd99813890e3d836b7ef3de3d5
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Prefer C++11 to C99 transcendentals
Removed workarounds that used, implemented, or approximately emulated
C99 functions
expm1l()
log1pl()
in favor of C++11 overloads
std::expm1(long double)
std::log1p(long double)
Rooting out all such workarounds utterly was important not only as a
general matter of hygiene, but also because numerous unit tests failed
on x86_86-linux-gnu due to redundant prototypes. For now at least,
the implementations in 'math_functors.hpp' actually call std::expm1l()
and std::log1pl(), so that the long double versions of these functions
are always used even with double arguments, to maintain equivalence to
prior revisions. Although the C++11 standard does not explicitly mention
these 'l'-suffixed functions, it says [26.8/4] that everything in C99
<math.h> is included (subsequent paragraphs describe only additions),
and [17.6.1.2/4] that they're in ns 'std'.
---
Makefile.am | 9 ----
config_ming323.hpp | 8 ----
configure.ac | 4 +-
expm1.c | 107 -----------------------------------------------
expm1.h | 34 ---------------
interest_rates.cpp | 4 +-
math_functors.hpp | 52 +++++++----------------
math_functors_test.cpp | 14 +++----
mortality_rates_test.cpp | 2 +-
objects.make | 8 ----
platform_dependent.hpp | 14 +------
11 files changed, 27 insertions(+), 229 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index c43865d..5a522f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -308,7 +308,6 @@ liblmi_common_sources = \
dbvalue.cpp \
death_benefits.cpp \
emit_ledger.cpp \
- expm1.c \
facets.cpp \
fenv_guard.cpp \
fenv_lmi.cpp \
@@ -617,7 +616,6 @@ test_commutation_functions_SOURCES = \
$(common_test_objects) \
commutation_functions.cpp \
commutation_functions_test.cpp \
- expm1.c \
timer.cpp
test_commutation_functions_CXXFLAGS = $(AM_CXXFLAGS)
@@ -706,7 +704,6 @@ test_global_settings_LDADD = \
test_gpt_SOURCES = \
$(common_test_objects) \
commutation_functions.cpp \
- expm1.c \
gpt_commutation_functions.cpp \
gpt_test.cpp \
ihs_irc7702.cpp \
@@ -745,7 +742,6 @@ test_input_SOURCES = \
dbdict.cpp \
dbnames.cpp \
dbvalue.cpp \
- expm1.c \
facets.cpp \
global_settings.cpp \
input.cpp \
@@ -798,7 +794,6 @@ test_istream_to_string_CXXFLAGS = $(AM_CXXFLAGS)
test_loads_SOURCES = \
$(common_test_objects) \
- expm1.c \
loads.cpp \
loads_test.cpp \
timer.cpp
@@ -816,7 +811,6 @@ test_materially_equal_CXXFLAGS = $(AM_CXXFLAGS)
test_math_functors_SOURCES = \
$(common_test_objects) \
- expm1.c \
math_functors_test.cpp \
timer.cpp
test_math_functors_CXXFLAGS = $(AM_CXXFLAGS)
@@ -846,7 +840,6 @@ test_miscellany_CXXFLAGS = $(AM_CXXFLAGS)
test_mortality_rates_SOURCES = \
$(common_test_objects) \
- expm1.c \
ihs_mortal.cpp \
mortality_rates_test.cpp
test_mortality_rates_CXXFLAGS = $(AM_CXXFLAGS)
@@ -927,7 +920,6 @@ test_product_file_SOURCES = \
dbdict.cpp \
dbnames.cpp \
dbvalue.cpp \
- expm1.c \
facets.cpp \
fund_data.cpp \
global_settings.cpp \
@@ -1133,7 +1125,6 @@ noinst_HEADERS = \
edit_mvc_docview_parameters.hpp \
emit_ledger.hpp \
exit_codes.hpp \
- expm1.h \
facets.hpp \
fenv_guard.hpp \
fenv_lmi.hpp \
diff --git a/config_ming323.hpp b/config_ming323.hpp
index 9284a4a..6eabd8b 100644
--- a/config_ming323.hpp
+++ b/config_ming323.hpp
@@ -41,14 +41,6 @@
// Version numbers are in 'include/_mingw.h' here:
// http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/mingw/?cvsroot=src
-#if 308 <= LMI_MINGW_VERSION
-# define LMI_COMPILER_PROVIDES_EXPM1L
-#endif // 308 <= LMI_MINGW_VERSION
-
-#if 202 <= LMI_MINGW_VERSION
-# define LMI_COMPILER_PROVIDES_LOG1PL
-#endif // 202 <= LMI_MINGW_VERSION
-
#if 200 <= LMI_MINGW_VERSION
# define LMI_COMPILER_PROVIDES_RINT
#endif // 200 <= LMI_MINGW_VERSION
diff --git a/configure.ac b/configure.ac
index 445b837..7be29bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -265,8 +265,6 @@ dnl existing code already uses these macros so continue to
use them instead of
dnl the usual HAVE_EXPM1/HAVE_LOG1P
AC_CHECK_FUNC(strtof, AC_DEFINE(LMI_COMPILER_PROVIDES_STRTOF, [1], [Define
this if you have strtof() function]))
AC_CHECK_FUNC(strtold, AC_DEFINE(LMI_COMPILER_PROVIDES_STRTOLD, [1], [Define
this if you have strtold() function]))
-AC_CHECK_FUNC(expm1l, AC_DEFINE(LMI_COMPILER_PROVIDES_EXPM1L, [1], [Define
this if you have expm1l() function]))
-AC_CHECK_FUNC(log1pl, AC_DEFINE(LMI_COMPILER_PROVIDES_LOG1PL, [1], [Define
this if you have log1pl() function]))
AC_CHECK_FUNC(rint, AC_DEFINE(LMI_COMPILER_PROVIDES_RINT, [1], [Define this if
you have rint() function]))
dnl === Library checks ===
@@ -582,7 +580,7 @@ if test "x$GXX" == "xyes"; then
dnl about "-funit-at-a-time is required for inlining of functions that are
dnl only called once" in debug builds
dnl
- dnl NB: this is needed for expm1.c so we must add it to CFLAGS too
+ dnl NB: this is needed for any '*.c' so we must add it to CFLAGS too
if test "$CLANG" != "yes"; then
LMI_C_CXX_ADD_IF_SUPPORTED(-fno-inline-functions-called-once)
fi
diff --git a/expm1.c b/expm1.c
deleted file mode 100644
index 10e1f8c..0000000
--- a/expm1.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* This is public domain. It's not
- * "Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
2014, 2015, 2016, 2017 Gregory W. Chicares"
- * but including that quoted string satisfies lmi's style-conformity
- * tests. Eventually this will become part of MinGW and cygwin, and
- * will then be removed from the lmi repository. Find the original at:
- *
- *
http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/mingw/mingwex/math/expm1.c?cvsroot=src
- *
- */
-
-/*
- * Written 2005 by Gregory W. Chicares <address@hidden>.
- * Adapted to double by Danny Smith <address@hidden>.
- * Public domain.
- *
- * F2XM1's input is constrained to (-1, +1), so the domain of
- * 'x * LOG2EL' is (-LOGE2L, +LOGE2L). Outside that domain,
- * delegating to exp() handles C99 7.12.6.3/2 range errors.
- *
- * Constants from moshier.net, file cephes/ldouble/constl.c,
- * are used instead of M_LN2 and M_LOG2E, which would not be
- * visible with 'gcc std=c99'. The use of these extended precision
- * constants also allows gcc to replace them with x87 opcodes.
- */
-
-/* Begin local GWC modifications. */
-#include "expm1.h"
-/* End local GWC modifications. */
-
-#include <math.h> /* expl() */
-/* Begin local GWC modifications:
- * #include "cephes_mconf.h"
- */
-#if !defined LMI_COMPILER_PROVIDES_EXPM1L
-# ifdef __GNUC__
-long double expm1l(long double x);
-#define LOGE2L 6.9314718055994530941723E-1L
-#define LOG2EL 1.4426950408889634073599E0L
-
-/*
- * http://savannah.nongnu.org/projects/lmi
- * email: <address@hidden>
- * snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
- *
- * End local GWC modifications. */
-
-long double expm1l(long double x) /* Local GWC modification: type changed. */
-{
- if (fabs(x) < LOGE2L)
- {
- x *= LOG2EL;
- __asm__("f2xm1" : "=t" (x) : "0" (x));
- return x;
- }
- else
- return exp(x) - 1.0;
-}
-
-double expm1(double x)
-{
- if (fabs(x) < LOGE2L)
- {
- x *= LOG2EL;
- __asm__("f2xm1" : "=t" (x) : "0" (x));
- return x;
- }
- else
- return exp(x) - 1.0;
-}
-
-#define SQRT2 1.41421356237309504880L
-
-long double log1pl(long double x)
-{
- if (fabs(x) < 1.0 - 0.5 * SQRT2)
- {
- __asm__("fldln2\n\t" "fxch\n\t" "fyl2xp1" : "=t" (x) : "0" (x));
- return x;
- }
- else
- return log(1.0 + x);
-}
-
-/* COMPILER !! Apparently como compiles this file as C++ despite its '.c'
- * extension: else 'extern "C"' wouldn't be required (or permitted).
- */
-
-/* Begin local GWC modifications. */
-# else // Not gcc.
- // COMPILER !! This workaround loses some accuracy.
-# ifdef __COMO__
- extern "C"
-# endif // __COMO__
- double expm1(double x) {return expm1l(x);}
-# endif // Not gcc.
-#endif // !defined LMI_COMPILER_PROVIDES_EXPM1L
-
-#if !defined LMI_COMPILER_PROVIDES_LOG1PL
-// COMPILER !! This workaround loses some accuracy.
-# ifdef __COMO__
- extern "C"
-# endif // __COMO__
- double log1p(double x) {return log1pl(x);}
-#endif // !defined LMI_COMPILER_PROVIDES_LOG1PL
-
-/* End local GWC modifications. */
-
diff --git a/expm1.h b/expm1.h
deleted file mode 100644
index dd0795b..0000000
--- a/expm1.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Provide expm1() [C99 7.12.6.3] for toolsets that lack it.
-//
-// Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
2016, 2017 Gregory W. Chicares.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software Foundation,
-// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-//
-// http://savannah.nongnu.org/projects/lmi
-// email: <address@hidden>
-// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
-
-// The include guard has an 'lmi_' prefix to avoid conflict, because
-// 'expm1_h' is an obvious name that libraries might use.
-
-#ifndef lmi_expm1_h
-#define lmi_expm1_h
-
-#include "config.hpp"
-
-// This header provides no prototype for exmpm1() because that
-// should be the job of <cmath>, once C++ adopts C99 extensions.
-
-#endif // lmi_expm1_h
-
diff --git a/interest_rates.cpp b/interest_rates.cpp
index 8d245d4..fbaff0b 100644
--- a/interest_rates.cpp
+++ b/interest_rates.cpp
@@ -95,8 +95,8 @@ namespace
// For the annual-effective method, transformation from annual to
// daily and back again by naive methods would lose considerable
// precision even when the spread and fee are zero, because i is
-// small relative to (1 + i). That is why expm1l() and log1pl() are
-// used instead of pow().
+// small relative to (1 + i). That is why std::expm1() and
+// std::log1p() are used instead of std::pow().
//
// If both spread and fee are zero, then the net rate should exactly
// equal the gross rate. However, those two rates would differ
diff --git a/math_functors.hpp b/math_functors.hpp
index 88cc781..3c9c015 100644
--- a/math_functors.hpp
+++ b/math_functors.hpp
@@ -28,34 +28,12 @@
#include "et_vector.hpp"
#include <algorithm> // std::max(), std::min()
-#include <cmath> // C99 expm1(), log1p()
+#include <cmath> // std::expm1(), std::log1p()
#include <functional>
#include <stdexcept>
#include <type_traits>
#include <vector>
-// For Comeau, implement expm1l() and log1pl() using type double, not
-// long double, because of an apparent incompatibility in the way
-// Comeau and MinGW pass long doubles.
-
-#if !defined LMI_COMPILER_PROVIDES_EXPM1L
-# if defined LMI_COMO_WITH_MINGW
-extern "C" double expm1(double);
-inline double expm1l(double x) {return expm1(x);}
-# else // !defined LMI_COMO_WITH_MINGW
-extern "C" long double expm1l(long double);
-# endif // !defined LMI_COMO_WITH_MINGW
-#endif // !defined LMI_COMPILER_PROVIDES_EXPM1L
-
-#if !defined LMI_COMPILER_PROVIDES_LOG1PL
-# if defined LMI_COMO_WITH_MINGW
-extern "C" double log1p(double);
-inline double log1pl(double x) {return log1p(x);}
-# else // !defined LMI_COMO_WITH_MINGW
-extern "C" long double log1pl(long double);
-# endif // !defined LMI_COMO_WITH_MINGW
-#endif // !defined LMI_COMPILER_PROVIDES_LOG1PL
-
// TODO ?? Write functors here for other refactorable uses of
// std::pow() found throughout the program.
@@ -125,7 +103,7 @@ struct mean
//
// Implementation note: greater accuracy and speed are obtained by
// applying the transformation
-// (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
+// (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
// to naive power-based formulas.
template<typename T, int n>
@@ -147,8 +125,8 @@ struct i_upper_n_over_n_from_i
static long double const reciprocal_n = 1.0L / n;
// naively: (1+i)^(1/n) - 1
- // substitute: (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
- long double z = expm1l(log1pl(i) * reciprocal_n);
+ // substitute: (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
+ long double z = std::expm1l(std::log1pl(i) * reciprocal_n);
return static_cast<T>(z);
}
};
@@ -173,8 +151,8 @@ struct i_from_i_upper_n_over_n
T operator()(T const& i) const
{
// naively: (1+i)^n - 1
- // substitute: (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
- long double z = expm1l(log1pl(i) * n);
+ // substitute: (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
+ long double z = std::expm1l(std::log1pl(i) * n);
return static_cast<T>(z);
}
};
@@ -210,8 +188,8 @@ struct d_upper_n_from_i
static long double const reciprocal_n = 1.0L / n;
// naively: n * (1 - (1+i)^(-1/n))
- // substitute: (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
- long double z = -n * expm1l(log1pl(i) * -reciprocal_n);
+ // substitute: (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
+ long double z = -n * std::expm1l(std::log1pl(i) * -reciprocal_n);
return static_cast<T>(z);
}
};
@@ -247,12 +225,12 @@ struct net_i_from_gross
// - (1+spread)^(1/n)
// - fee *(1/n)
// )^n - 1
- // substitute: (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
- long double z = expm1l
+ // substitute: (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
+ long double z = std::expm1l
(
- n * log1pl
- ( expm1l(reciprocal_n * log1pl(i))
- - expm1l(reciprocal_n * log1pl(spread))
+ n * std::log1pl
+ ( std::expm1l(reciprocal_n * std::log1pl(i))
+ - std::expm1l(reciprocal_n * std::log1pl(spread))
- reciprocal_n * fee
)
);
@@ -312,8 +290,8 @@ struct coi_rate_from_q
{
static long double const reciprocal_12 = 1.0L / 12;
// naively: 1 - (1-q)^(1/12)
- // substitute: (1+i)^n - 1 <-> expm1l(log1pl(i) * n)
- long double monthly_q = -expm1l(log1pl(-q) * reciprocal_12);
+ // substitute: (1+i)^n - 1 <-> std::expm1(std::log1p(i) * n)
+ long double monthly_q = -std::expm1l(std::log1pl(-q) *
reciprocal_12);
if(1.0L == monthly_q)
{
throw std::logic_error("Monthly q equals unity.");
diff --git a/math_functors_test.cpp b/math_functors_test.cpp
index 77e6ef3..0fa63f5 100644
--- a/math_functors_test.cpp
+++ b/math_functors_test.cpp
@@ -159,9 +159,9 @@ void sample_results()
fenv_precision(fe_ldblprec);
std::cout
<< std::setprecision(20)
- << " long double precision, expm1l and log1pl\n "
+ << " long double precision, std::expm1 and std::log1p\n "
<< net_i_from_gross<double,365>()(0.0, 0.004, 0.0) << '\n'
- << " long double precision, pow\n "
+ << " long double precision, std::pow\n "
<< net_i_from_gross_naive<double,365>()(0.0, 0.004, 0.0) << '\n'
;
@@ -170,9 +170,9 @@ void sample_results()
#endif // defined LMI_X87
std::cout
<< std::setprecision(20)
- << " double precision, expm1l and log1pl\n "
+ << " double precision, std::expm1 and std::log1p\n "
<< net_i_from_gross<double,365> ()(0.0, 0.004, 0.0) << '\n'
- << " double precision, pow\n "
+ << " double precision, std::pow\n "
<< net_i_from_gross_naive<double,365>()(0.0, 0.004, 0.0) << '\n'
;
@@ -183,7 +183,7 @@ void sample_results()
// different implementations.
// This implementation naively uses std::pow(); it is both slower and
-// less inaccurate than an alternative using expm1l() and log1pl().
+// less inaccurate than an alternative using std::expm1() and std::log1p().
void mete0()
{
double volatile x;
@@ -207,8 +207,8 @@ void mete1()
void assay_speed()
{
- std::cout << " Speed test: pow \n " << TimeAnAliquot(mete0) << '\n';
- std::cout << " Speed test: expm1l\n " << TimeAnAliquot(mete1) << '\n';
+ std::cout << " Speed test: std::pow \n " << TimeAnAliquot(mete0) <<
'\n';
+ std::cout << " Speed test: std::expm1\n " << TimeAnAliquot(mete1) <<
'\n';
}
int test_main(int, char*[])
diff --git a/mortality_rates_test.cpp b/mortality_rates_test.cpp
index b71b565..bf478a8 100644
--- a/mortality_rates_test.cpp
+++ b/mortality_rates_test.cpp
@@ -57,7 +57,7 @@ std::vector<double> annual_rates()
/// qm = 1 - (1-q)^(1/12)
/// qm = qm / (1-qm)
/// diverges even in the tenth significant digit. Values given here
-/// use expm1() and log1p() for better accuracy.
+/// use std::expm1() and std::log1p() for better accuracy.
std::vector<double> monthly_rates()
{
diff --git a/objects.make b/objects.make
index 6e2105e..b3f0739 100644
--- a/objects.make
+++ b/objects.make
@@ -197,7 +197,6 @@ common_common_objects := \
dbvalue.o \
death_benefits.o \
emit_ledger.o \
- expm1.o \
facets.o \
fenv_guard.o \
fenv_lmi.o \
@@ -549,7 +548,6 @@ commutation_functions_test$(EXEEXT): \
$(common_test_objects) \
commutation_functions.o \
commutation_functions_test.o \
- expm1.o \
timer.o \
configurable_settings_test$(EXEEXT): \
@@ -625,7 +623,6 @@ global_settings_test$(EXEEXT): \
gpt_test$(EXEEXT): \
$(common_test_objects) \
commutation_functions.o \
- expm1.o \
gpt_commutation_functions.o \
gpt_test.o \
ihs_irc7702.o \
@@ -661,7 +658,6 @@ input_test$(EXEEXT): \
dbdict.o \
dbnames.o \
dbvalue.o \
- expm1.o \
facets.o \
global_settings.o \
input.o \
@@ -709,7 +705,6 @@ istream_to_string_test$(EXEEXT): \
loads_test$(EXEEXT): \
$(common_test_objects) \
- expm1.o \
loads.o \
loads_test.o \
timer.o \
@@ -724,7 +719,6 @@ materially_equal_test$(EXEEXT): \
math_functors_test$(EXEEXT): \
$(common_test_objects) \
- expm1.o \
math_functors_test.o \
timer.o \
@@ -750,7 +744,6 @@ miscellany_test$(EXEEXT): \
mortality_rates_test$(EXEEXT): \
$(common_test_objects) \
- expm1.o \
ihs_mortal.o \
mortality_rates_test.o \
@@ -823,7 +816,6 @@ product_file_test$(EXEEXT): \
dbdict.o \
dbnames.o \
dbvalue.o \
- expm1.o \
facets.o \
fund_data.o \
global_settings.o \
diff --git a/platform_dependent.hpp b/platform_dependent.hpp
index a2adcea..99090b0 100644
--- a/platform_dependent.hpp
+++ b/platform_dependent.hpp
@@ -46,9 +46,7 @@
// that are useful but absent from the standard language: for example,
// it is difficult to implement cgi-bin without putenv(). Some others:
// _wcsdup(), fileno(), strcasecmp(), strdup()
-// should be avoided in general, but are required by wx. Still others,
-// like expm1l(), are in C99 but not C++98; the way their prototypes
-// are provided for gcc varies by platform.
+// should be avoided in general, but are required by wx.
#if defined __GNUC__ && defined __STRICT_ANSI__
# define LMI_GNUC_STRICT_ANSI
@@ -93,16 +91,6 @@
// means, locally, wherever it's needed. As of 2017-04, getch() is no
// longer used; this paragraph is kept lest it be reintroduced.
-// GNU/Linux (but not MinGW) requires including certain headers while
-// __STRICT_ANSI__ is not defined in order to get prototypes for
-// certain functions, for C++ with '-std=c++98':
-// math.h: expm1l() and log1pl()
-// Use the C instead of the C++ system header so that the present file
-// can be included in C as well as C++ translation units, which is
-// temporarily useful until 'expm1.c' can be removed.
-
-#include <math.h>
-
// Although gcc may once have defined __STRICT_ANSI__ differently:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=3199
// its intended value now is 1: