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
This commit is contained in:
John Ralls 2018-11-27 22:11:36 +09:00
parent 536606a89c
commit 61cd7999f3

View File

@ -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<RoundType::half_down>)
{
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>(GncInt128 num, GncInt128 den, GncInt128 rem,
RT2T<RoundType::half_down>)
{
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<RoundType::half_up>)
{
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>(GncInt128 num, GncInt128 den, GncInt128 rem,
RT2T<RoundType::half_up>)
{
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<RoundType::bankers>)
{
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>(GncInt128 num, GncInt128 den, GncInt128 rem,
RT2T<RoundType::bankers>)
{
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__