Introduce proper integer rounding function

This fixes a failing unit test with 32bit gcc 4.9.3 and -O2 optimization:
It computed 9953 instead of 9954 for Length::inPixels() of value 2342.
The reason for this is probably different rounding behaviour caused by storing
the unrounded value in a processor register (uses 80bit accuracy) vs. writing
it back to memory (uses 64bit accuracy). The unrounded value is very close to
9953.5 (which is not representable as an exact IEEE floating point value).

Apart from that, having a proper function for rounding makes the code more
readable, and has the nice side effect to make Length::inPB() work for
negative lengths as well.
This commit is contained in:
Georg Baum 2016-02-17 21:31:37 +01:00
parent fc459bd977
commit c0ce79452f
6 changed files with 36 additions and 25 deletions

View File

@ -23,6 +23,7 @@
#include "support/docstream.h" #include "support/docstream.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h"
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
@ -215,7 +216,7 @@ int Length::inPixels(int text_width, int em_width_base) const
double const text_width_in = text_width / (zoom * dpi); double const text_width_in = text_width / (zoom * dpi);
double const result = zoom * dpi * inInch(text_width_in, em_width_in); double const result = zoom * dpi * inInch(text_width_in, em_width_in);
return static_cast<int>(result + ((result >= 0) ? 0.5 : -0.5)); return support::iround(result);
} }
@ -311,7 +312,7 @@ int Length::inBP() const
double const text_width_in = 210.0 / 2.54; // assume A4 double const text_width_in = 210.0 / 2.54; // assume A4
double const em_width_in = 10.0 / 72.27; double const em_width_in = 10.0 / 72.27;
double result = 72.0 * inInch(text_width_in, em_width_in); double result = 72.0 * inInch(text_width_in, em_width_in);
return static_cast<int>(result + 0.5); return support::iround(result);
} }

View File

@ -438,7 +438,7 @@ docstring InsetGraphics::createDocBookAttributes() const
if (!params().scale.empty() && !float_equal(scl, 0.0, 0.05)) { if (!params().scale.empty() && !float_equal(scl, 0.0, 0.05)) {
if (!float_equal(scl, 100.0, 0.05)) if (!float_equal(scl, 100.0, 0.05))
options << " scale=\"" options << " scale=\""
<< static_cast<int>( (scl) + 0.5 ) << support::iround(scl)
<< "\" "; << "\" ";
} else { } else {
if (!params().width.zero()) { if (!params().width.zero()) {

View File

@ -26,6 +26,7 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h"
#include "support/textutils.h" #include "support/textutils.h"
@ -77,9 +78,9 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
} }
int const em = mathed_font_em(mi.base.font); int const em = mathed_font_em(mi.base.font);
if (isBinaryOp(char_)) if (isBinaryOp(char_))
dim.wid += static_cast<int>(0.5*em+0.5); dim.wid += support::iround(0.5 * em);
else if (char_ == '\'') else if (char_ == '\'')
dim.wid += static_cast<int>(0.1667*em+0.5); dim.wid += support::iround(0.1667 * em);
#else #else
whichFont(font_, code_, mi); whichFont(font_, code_, mi);
dim = theFontMetrics(font_).dimension(char_); dim = theFontMetrics(font_).dimension(char_);
@ -95,9 +96,9 @@ void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
//lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl; //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl;
int const em = mathed_font_em(pi.base.font); int const em = mathed_font_em(pi.base.font);
if (isBinaryOp(char_)) if (isBinaryOp(char_))
x += static_cast<int>(0.25*em+0.5); x += support::iround(0.25 * em);
else if (char_ == '\'') else if (char_ == '\'')
x += static_cast<int>(0.0833*em+0.5); x += support::iround(0.0833 * em);
#if 1 #if 1
if (char_ == '=' && has_math_fonts) { if (char_ == '=' && has_math_fonts) {
FontSetChanger dummy(pi.base, "cmr"); FontSetChanger dummy(pi.base, "cmr");

View File

@ -21,6 +21,7 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/docstream.h" #include "support/docstream.h"
#include "support/lyxlib.h"
#include "support/textutils.h" #include "support/textutils.h"
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
@ -80,9 +81,9 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
} }
// seperate things a bit // seperate things a bit
if (isRelOp()) if (isRelOp())
dim.wid += static_cast<int>(0.5 * em + 0.5); dim.wid += support::iround(0.5 * em);
else else
dim.wid += static_cast<int>(0.1667 * em + 0.5); dim.wid += support::iround(0.1667 * em);
scriptable_ = false; scriptable_ = false;
if (mi.base.style == LM_ST_DISPLAY) if (mi.base.style == LM_ST_DISPLAY)
@ -106,9 +107,9 @@ void InsetMathSymbol::draw(PainterInfo & pi, int x, int y) const
std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
int const em = mathed_font_em(pi.base.font); int const em = mathed_font_em(pi.base.font);
if (isRelOp()) if (isRelOp())
x += static_cast<int>(0.25*em+0.5); x += support::iround(0.25 * em);
else else
x += static_cast<int>(0.0833*em+0.5); x += support::iround(0.0833 * em);
FontSetChanger dummy(pi.base, from_ascii(font)); FontSetChanger dummy(pi.base, from_ascii(font));
pi.draw(x, y - h_, sym_->draw); pi.draw(x, y - h_, sym_->draw);

View File

@ -16,13 +16,13 @@
#include "support/convert.h" #include "support/convert.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/lyxlib.h"
#include "support/qstring_helpers.h" #include "support/qstring_helpers.h"
#include "support/lassert.h" #include "support/lassert.h"
#include <QString> #include <QString>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
@ -1400,18 +1400,6 @@ int findToken(char const * const str[], string const & search_token)
} }
#ifdef _MSC_VER
// Replacement for C99 function lround()
double round(double x)
{
if (x < 0)
return ceil(x - 0.5);
else
return floor(x + 0.5);
}
#endif
std::string formatFPNumber(double x) std::string formatFPNumber(double x)
{ {
// Need manual tweaking, QString::number(x, 'f', 16) does not work either // Need manual tweaking, QString::number(x, 'f', 16) does not work either
@ -1420,7 +1408,7 @@ std::string formatFPNumber(double x)
// Prevent outputs of 23.4200000000000017 but output small numbers // Prevent outputs of 23.4200000000000017 but output small numbers
// with at least 6 significant digits. // with at least 6 significant digits.
double const logarithm = log10(fabs(x)); double const logarithm = log10(fabs(x));
os << std::setprecision(max(6 - static_cast<int>(round(logarithm)), 0)) << x; os << std::setprecision(max(6 - iround(logarithm), 0)) << x;
string result = os.str(); string result = os.str();
if (result.find('.') != string::npos) { if (result.find('.') != string::npos) {
result = rtrim(result, "0"); result = rtrim(result, "0");

View File

@ -15,6 +15,20 @@
#ifndef LYX_LIB_H #ifndef LYX_LIB_H
#define LYX_LIB_H #define LYX_LIB_H
// always include <math.h> (also with MSVC), to avoid compiler specific side effects
#include <math.h>
#ifdef _MSC_VER
/// Replacement for C99 round()
inline double round(double x)
{
if (x < 0)
return ceil(x - 0.5);
else
return floor(x + 0.5);
}
#endif
namespace lyx { namespace lyx {
namespace support { namespace support {
@ -34,6 +48,12 @@ inline bool float_equal(double var, double number, double error)
return (number - error <= var && var <= number + error); return (number - error <= var && var <= number + error);
} }
/// round \p x to nearest integer
inline int iround(double x)
{
return static_cast<int>(round(x));
}
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx