Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistently use Real datatype #1400

Merged
merged 5 commits into from
Jun 13, 2022

Conversation

auto-differentiation-dev
Copy link
Contributor

This replaces many stray uses of the double datatype with QuantLib::Real, to ensure it is used consistently throughout the library and test suite.

In particular:

  • it replaces double with Real
  • it initialises accumulators in std::inner_product and std::accumulate with Real literals, to ensure the values are aggregated with the correct data type internally
  • it avoids constexpr Real x = ... statements, as with the global typedef we cannot generally assume that Real can in fact be initialised in a constexpr

@boring-cyborg
Copy link

boring-cyborg bot commented Jun 8, 2022

Thanks for opening this pull request! It might take a while before we look at it, so don't worry if there seems to be no feedback. We'll get to it.

@CLAassistant
Copy link

CLAassistant commented Jun 8, 2022

CLA assistant check
All committers have signed the CLA.

@auto-differentiation-dev auto-differentiation-dev marked this pull request as ready for review June 8, 2022 15:25
@@ -65,7 +65,7 @@ namespace QuantLib {
//calculate payoff discount factor assuming continuous compounding
DiscountFactor discount = riskFreeDiscount(t1);
Real result = 0;
constexpr Real minusInf = -std::numeric_limits<Real>::infinity();
const Real minusInf = -std::numeric_limits<Real>::infinity();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These and the others are constants, so I'd use double as you did in the change right above. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We thought about that, but there could be an issue with this in the remote (admittedly unlikely) case that Real is something else than double with a different representation of infinity. If we define a constant with double-infinity, and then use it in calculations with Real, it may become an issue.

The case above is different, as it's just a regular number, not infinity.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep the double constants now and think about the unlikely case if it comes up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that is just pushed and fixed as suggested.

@@ -185,6 +185,15 @@ namespace QuantLib {
Real freq_;
};

// helper to avoid relying on implicit conversion to Rate
inline Rate operator-(const InterestRate& a, const InterestRate& b) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I didn't realize one could add or subtract InterestRate instances. I would probably remove these operators and write an explicit call to rate at the point of use, to avoid adding or subtracting rates with different conventions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to agree on that point. However, in that case I would remove the implicit conversion operator in the InterestRate class to Rate (i.e. Real) altogether. Otherwise such operations can be added without developers noticing.

One example where this is used is in ql/experimental/processes/extendedblackscholesprocess.cpp, but there are 15 uses in total throughout the library.

What do you think?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree, but removing the implicit conversion would break backward compatibility.

Removing these operators at least would help us find and avoid implicit conversions in subtractions and additions, which are more at risk of inconsistency because they involve two different rates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's just pushed - we changed it at the point of use to an explicit call to rate and removed those operators.

@coveralls
Copy link

coveralls commented Jun 8, 2022

Coverage Status

Coverage remained the same at 71.145% when pulling ad19ccc on xcelerit:feature/consistent-real into 256cc86 on lballabio:master.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 17 potential problems in the proposed changes. Check the Files changed tab for more details.

@sweemer
Copy link
Contributor

sweemer commented Jun 9, 2022

it avoids constexpr Real x = ... statements, as with the global typedef we cannot generally assume that Real can in fact be initialised in a constexpr

As mentioned by Luigi, constexpr local variables can be kept as doubles. But in principle the type defined as QL_REAL should support constexpr initialization because other defines such as QL_MIN_REAL and QL_EPSILON are defined in terms of QL_REAL using constexpr functions from std::numeric_limits. If there are no compile errors yet then it probably means that these defines are not used themselves in a constexpr context, but it doesn't necessarily mean that they won't be at some point in the future.

@sweemer
Copy link
Contributor

sweemer commented Jun 9, 2022

Also, I think you will need to add a CI job to pass in the specific type of Real that you care about to make sure that there are no compilation errors after any future commits.

@lballabio
Copy link
Owner

other defines such as QL_MIN_REAL and QL_EPSILON are defined in terms of QL_REAL using constexpr functions from std::numeric_limits.

Using std::numeric_limits with the new type would require writing a new specialization, and the methods defined there might not be constexpr, I guess?

@sweemer
Copy link
Contributor

sweemer commented Jun 9, 2022

Using std::numeric_limits with the new type would require writing a new specialization, and the methods defined there might not be constexpr, I guess?

Right, so if somebody later makes a change to the library to use QL_EPSILON at compile-time then the non-constexpr specialization will no longer be good enough.

I think that preserving the ability to use the QL_* values at compile time with QL_REAL typedef'ed to double outweighs the obligation (if one even exists) to support any possible typedef of QL_REAL. So in my opinion the burden is on the user to provide constexpr specializations of std::numeric_limits<Real>::... if the library requires it at any point in the future.

@auto-differentiation-dev
Copy link
Contributor Author

Also, I think you will need to add a CI job to pass in the specific type of Real that you care about to make sure that there are no compilation errors after any future commits.

This is a good suggestion indeed @sweemer - that would prevent any stray use of double moving forward. We're open to do that with a standalone Real replacement type for testing purposes, but that does not need to be part of this PR. We can do that at a later stage.

@auto-differentiation-dev
Copy link
Contributor Author

Regarding the constexpr discussion:

In general, a Real typedef to something other than a built-in floating point type (float or double) typically means that constexpr intialisations may not work, i.e. compile-time constants for QL_EPSILON etc are not possible.

However, we suggest when/if this issue comes up, QL_EPSILON etc may be defined as an "underlying" built-in floating point type, where constexpr works. For example, if a struct is used which internally wraps double, QL_EPSILON etc may be defined in terms of double (not Real). And a specialisation of std::numeric_limits<Real> needs to be provided that returns double. This is definitely possible, wouldn't break semantics, and keeps compile-time logic working.

However, we think for this PR no further changes are needed and we can keep the constants as double as per Luigi's suggestion. The above suggestion could be put in place when it is needed.

@lballabio lballabio added this to the Release 1.27 milestone Jun 13, 2022
@lballabio lballabio merged commit 42dc7d0 into lballabio:master Jun 13, 2022
@boring-cyborg
Copy link

boring-cyborg bot commented Jun 13, 2022

Congratulations on your first merged pull request!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants