From 61cd7999f34a3976fd45bf4668d7012ab3ca4835 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 27 Nov 2018 22:11:36 +0900 Subject: [PATCH] Fix half-up, half-down, and banker's rounding for negative numbers. We need to compare the magnitudes of the remainder and the denominator in order to round negative numbers correctly. Note that while gnc_numeric is constrained to a positive denominator the C++ rounding functions cannot assume that constraint in all cases. Combined with the previous commit, this fixes Bug 796949 - Incorrect conversion of 0,01 USD to EUR --- libgnucash/engine/gnc-rational-rounding.hpp | 41 +++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/libgnucash/engine/gnc-rational-rounding.hpp b/libgnucash/engine/gnc-rational-rounding.hpp index f5cdb50310..692301701e 100644 --- a/libgnucash/engine/gnc-rational-rounding.hpp +++ b/libgnucash/engine/gnc-rational-rounding.hpp @@ -23,6 +23,7 @@ #ifndef __GNC_RATIONAL_ROUNDING_HPP__ #define __GNC_RATIONAL_ROUNDING_HPP__ #include "gnc-numeric.h" +#include "gnc-int128.hpp" enum class RoundType { @@ -105,7 +106,18 @@ round(T num, T den, T rem, RT2T) { if (rem == 0) return num; - if (rem * 2 > den) + if (std::abs(rem * 2) > std::abs(den)) + return num + (num < 0 ? -1 : 1); + return num; +} + +template <> inline GncInt128 +round(GncInt128 num, GncInt128 den, GncInt128 rem, + RT2T) +{ + if (rem == 0) + return num; + if (rem.abs() * 2 > den.abs()) return num + (num < 0 ? -1 : 1); return num; } @@ -115,7 +127,18 @@ round(T num, T den, T rem, RT2T) { if (rem == 0) return num; - if (rem * 2 >= den) + if (std::abs(rem) * 2 >= std::abs(den)) + return num + (num < 0 ? -1 : 1); + return num; +} + +template <> inline GncInt128 +round(GncInt128 num, GncInt128 den, GncInt128 rem, + RT2T) +{ + if (rem == 0) + return num; + if (rem.abs() * 2 >= den.abs()) return num + (num < 0 ? -1 : 1); return num; } @@ -125,9 +148,21 @@ round(T num, T den, T rem, RT2T) { if (rem == 0) return num; - if (rem * 2 > den || (rem * 2 == den && num % 2)) + if (std::abs(rem * 2) > std::abs(den) || + (std::abs(rem * 2) == std::abs(den) && num % 2)) return num += (num < 0 ? -1 : 1); return num; } +template<> inline GncInt128 +round(GncInt128 num, GncInt128 den, GncInt128 rem, + RT2T) +{ + if (rem == 0) + return num; + if (rem.abs() * 2 > den.abs() || + (rem.abs() * 2 == den.abs() && num % 2)) + return num += (num < 0 ? -1 : 1); + return num; +} #endif //__GNC_RATIONAL_ROUNDING_HPP__