From: Jens Steinhauser Date: Sat, 29 Aug 2015 14:57:37 +0000 (+0200) Subject: format_si_value(): Use the timestamp type in the calculation X-Git-Tag: pulseview-0.3.0~125 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=f52be90d7f12fc07f71b75a104abefd02aa03ed2;p=pulseview.git format_si_value(): Use the timestamp type in the calculation --- diff --git a/pv/util.cpp b/pv/util.cpp index 192a2e06..78b32af7 100644 --- a/pv/util.cpp +++ b/pv/util.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -34,49 +35,98 @@ using namespace Qt; namespace pv { namespace util { -static const QString SIPrefixes[17] = - {"y", "z", "a", "f", "p", "n", QChar(0x03BC), "m", "", "k", "M", "G", - "T", "P", "E", "Z", "Y"}; -const int FirstSIPrefix = 8; -const int FirstSIPrefixPower = -(FirstSIPrefix * 3); +static const QString SIPrefixes[17] = { + "y", "z", + "a", "f", "p", + "n", QChar(0x03BC), "m", + "", + "k", "M", "G", + "T", "P", "E", + "Z", "Y"}; +const int EmptySIPrefix = 8; +const int FirstSIPrefixPower = -(EmptySIPrefix * 3); const double MinTimeDelta = 1e-15; // Anything below 1 fs can be considered zero -static QString format_si_value(double v, QString unit, int prefix, +// Insert the timestamp value into the stream in fixed-point notation +// (and honor the precision) +static QTextStream& operator<<(QTextStream& stream, const Timestamp& t) +{ + // The multiprecision types already have a function and a stream insertion + // operator to convert them to a string, however these functions abuse a + // precision value of zero to print all available decimal places instead of + // none, and the boost authors refuse to fix this because they don't want + // to break buggy code that relies on this bug. + // (https://svn.boost.org/trac/boost/ticket/10103) + // Therefore we have to work around the case where precision is zero. + + int precision = stream.realNumberPrecision(); + + std::ostringstream ss; + ss << std::fixed; + + if (stream.numberFlags() & QTextStream::ForceSign) { + ss << std::showpos; + } + + if (0 == precision) { + ss + << std::setprecision(1) + << round(t); + } else { + ss + << std::setprecision(precision) + << t; + } + + std::string str(ss.str()); + if (0 == precision) { + // remove the separator and the unwanted decimal place + str.resize(str.size() - 2); + } + + return stream << QString::fromStdString(str); +} + +QString format_si_value(const Timestamp& v, QString unit, int prefix, unsigned int precision, bool sign) { if (prefix < 0) { - int exp = -FirstSIPrefixPower; - - prefix = 0; - while ((fabs(v) * pow(10.0, exp)) > 999.0 && - prefix < (int)(countof(SIPrefixes) - 1)) { - prefix++; - exp -= 3; + // No prefix given, calculate it + + if (v.is_zero()) { + prefix = EmptySIPrefix; + } else { + int exp = -FirstSIPrefixPower; + + prefix = 0; + while ((fabs(v) * pow(10.0, exp)) > 999.0 && + prefix < (int)(countof(SIPrefixes) - 1)) { + prefix++; + exp -= 3; + } } } assert(prefix >= 0); assert(prefix < (int)countof(SIPrefixes)); - const double multiplier = pow(10.0, + const Timestamp multiplier = pow(Timestamp(10), (int)- prefix * 3 - FirstSIPrefixPower); QString s; QTextStream ts(&s); - if (sign) + if (sign && !v.is_zero()) ts << forcesign; - ts << fixed << qSetRealNumberPrecision(precision) << - (v * multiplier) << " " << SIPrefixes[prefix] << unit; + ts + << qSetRealNumberPrecision(precision) + << (v * multiplier) + << ' ' + << SIPrefixes[prefix] + << unit; return s; } -QString format_si_value(const Timestamp& v, QString unit, int prefix, - unsigned int precision, bool sign) -{ - return format_si_value(v.convert_to(), unit, prefix, precision, sign); -} - static QString pad_number(unsigned int number, int length) { return QString("%1").arg(number, length, 10, QChar('0')); @@ -156,7 +206,7 @@ static QString format_time_with_si(double t, QString unit, int prefix, -(prefix * 3 + pv::util::FirstSIPrefixPower); const unsigned int relative_prec = - (prefix >= pv::util::FirstSIPrefix) ? precision : + (prefix >= EmptySIPrefix) ? precision : std::max((int)(precision - prefix_order), 0); return format_si_value(t, unit, prefix, relative_prec); @@ -189,7 +239,7 @@ QString format_time(const Timestamp& t, int prefix, TimeUnit unit, unsigned int QString format_second(const Timestamp& second) { - return format_si_value(second.convert_to(), "s", -1, 0, false); + return format_si_value(second, "s", -1, 0, false); } } // namespace util diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 726d7070..6e41d39a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,6 +73,7 @@ set(pulseview_TEST_SOURCES data/analogsegment.cpp data/logicsegment.cpp test.cpp + util.cpp ) # This list includes only QObject derived class headers. diff --git a/test/util.cpp b/test/util.cpp new file mode 100644 index 00000000..1e33790b --- /dev/null +++ b/test/util.cpp @@ -0,0 +1,144 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2015 Jens Steinhauser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#include + +#include "pv/util.hpp" + +using namespace pv::util; +using ts = pv::util::Timestamp; + +namespace { + QChar mu = QChar(0x03BC); +} + +std::ostream& operator<<(std::ostream& stream, const QString& str) +{ + return stream << str.toUtf8().data(); +} + +BOOST_AUTO_TEST_SUITE(UtilTest) + +BOOST_AUTO_TEST_CASE(format_si_value_test) +{ + // check prefix calculation + + BOOST_CHECK_EQUAL(format_si_value(ts("0"), "V"), "0 V"); + + BOOST_CHECK_EQUAL(format_si_value(ts("1e-24"), "V"), "+1 yV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-23"), "V"), "+10 yV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-22"), "V"), "+100 yV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-21"), "V"), "+1 zV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-20"), "V"), "+10 zV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-19"), "V"), "+100 zV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-18"), "V"), "+1 aV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-17"), "V"), "+10 aV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-16"), "V"), "+100 aV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-15"), "V"), "+1 fV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-14"), "V"), "+10 fV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-13"), "V"), "+100 fV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-12"), "V"), "+1 pV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-11"), "V"), "+10 pV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-10"), "V"), "+100 pV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-9"), "V"), "+1 nV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-8"), "V"), "+10 nV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-7"), "V"), "+100 nV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-6"), "V"), QString("+1 ") + mu + "V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-5"), "V"), QString("+10 ") + mu + "V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-4"), "V"), QString("+100 ") + mu + "V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-3"), "V"), "+1 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-2"), "V"), "+10 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-1"), "V"), "+100 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e0"), "V"), "+1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e1"), "V"), "+10 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e2"), "V"), "+100 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e3"), "V"), "+1 kV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e4"), "V"), "+10 kV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e5"), "V"), "+100 kV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e6"), "V"), "+1 MV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e7"), "V"), "+10 MV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e8"), "V"), "+100 MV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e9"), "V"), "+1 GV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e10"), "V"), "+10 GV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e11"), "V"), "+100 GV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e12"), "V"), "+1 TV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e13"), "V"), "+10 TV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e14"), "V"), "+100 TV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e15"), "V"), "+1 PV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e16"), "V"), "+10 PV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e17"), "V"), "+100 PV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e18"), "V"), "+1 EV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e19"), "V"), "+10 EV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e20"), "V"), "+100 EV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e21"), "V"), "+1 ZV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e22"), "V"), "+10 ZV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e23"), "V"), "+100 ZV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e24"), "V"), "+1 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e25"), "V"), "+10 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e26"), "V"), "+100 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e27"), "V"), "+1000 YV"); + + BOOST_CHECK_EQUAL(format_si_value(ts("1234"), "V"), "+1 kV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1234"), "V", 9, 3), "+1.234 kV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1234.5678"), "V"), "+1 kV"); + + // check if a given prefix is honored + + BOOST_CHECK_EQUAL(format_si_value(ts("1e-24"), "V", 0), "+1 yV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-21"), "V", 0), "+1000 yV"); + BOOST_CHECK_EQUAL(format_si_value(ts("0"), "V", 0), "0 yV"); + + BOOST_CHECK_EQUAL(format_si_value(ts("1e-4"), "V", 7), "+0 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-4"), "V", 7, 1), "+0.1 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1000"), "V", 7), "+1000000 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("0"), "V", 7), "0 mV"); + + BOOST_CHECK_EQUAL(format_si_value(ts("1e-1"), "V", 8), "+0 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-1"), "V", 8, 1), "+0.1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e-1"), "V", 8, 2), "+0.10 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1"), "V", 8), "+1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e1"), "V", 8), "+10 V"); + + BOOST_CHECK_EQUAL(format_si_value(ts("1e23"), "V", 16), "+0 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e23"), "V", 16, 1), "+0.1 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1e27"), "V", 16), "+1000 YV"); + BOOST_CHECK_EQUAL(format_si_value(ts("0"), "V", 16), "0 YV"); + + // check precision + + BOOST_CHECK_EQUAL(format_si_value(ts("1.2345678"), "V"), "+1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.4"), "V"), "+1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.5"), "V"), "+2 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.9"), "V"), "+2 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.2345678"), "V", -1, 2), "+1.23 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.2345678"), "V", -1, 3), "+1.235 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.2345678"), "V", 7, 3), "+1234.568 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.2345678"), "V", 7, 0), "+1235 mV"); + BOOST_CHECK_EQUAL(format_si_value(ts("1.2"), "V", -1, 3), "+1.200 V"); + + // check sign + + BOOST_CHECK_EQUAL(format_si_value(ts("-1"), "V", 8, 0, true), "-1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("-1"), "V", 8, 0, false), "-1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1"), "V", 8, 0, true), "+1 V"); + BOOST_CHECK_EQUAL(format_si_value(ts("1"), "V", 8, 0, false), "1 V"); +} + +BOOST_AUTO_TEST_SUITE_END()