Witam,
Poszukuję gotowego rozwiązania na konwersję liczb do postaci inżynierskiej, czyli np.:
0,000047 -> 47u
840000000 -> 840M
Istnieje coś takiego w bibliotekach do C++?
Dzięki z góry.
Witam,
Poszukuję gotowego rozwiązania na konwersję liczb do postaci inżynierskiej, czyli np.:
0,000047 -> 47u
840000000 -> 840M
Istnieje coś takiego w bibliotekach do C++?
Dzięki z góry.
Watpie, ale to raptem pare linijek, nie szybciej samemu napisac?
do takich błahostek bibliotek się nie robi
Lucky you.
Kiedyś sam robiłem taką metodę. Masz ją poniżej. Załączam nawet test case-y :P.
Niestety nie przechodzą wszystkie (6 faill-uje), ale ponieważ zachodzi to tylko dla liczb większych niż 1027 to olałem te błędy.
Jeśli usuniesz tego bug-a (zdaje się, że źle sprawdzam zakres tablicy i jest jeszcze mały problem z zaokrąglaniem dla szczególnego przypadku) to pochwal się jak to trzeba poprawić. Mi się nie chce.
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <string>
using namespace std;
string ToEngineer( double value, int prec = 3 )
{
// jokto, zepto, atto, femto, pico, nano, micro, mili,
// kilo, mega, giga, tera, peta, eksa, zetta, jotta
static const char UnitPrefixes[] = "yzafpnum\0kMGTPEZY";
static const int SmallestUnitPrefix = -24; // atto = 10^-24
static const int BigestBigestUnitPrefix = 24; // tera = 10^24
static const int UnitPrefIndexShift = SmallestUnitPrefix / 3;
static const int UnitPrefIndexMax = BigestBigestUnitPrefix / 3;
if( value == 0 )
return "0";
ostringstream result;
bool sign;
if( sign = (value < 0) )
{
value =- value;
result << '-';
}
int hiest = static_cast<int>( floor( log10( value ) ) );
int index = max( SmallestUnitPrefix, min( BigestBigestUnitPrefix, hiest ) );
if( index < 0 )
index -= 2;
index /=3;
value *= pow( 1000.0, -index );
if( value < 1000 )
if( 1000.0 - value < 500.0*pow( 0.1, prec-1 ) )
{
++index;
if( index > UnitPrefIndexMax )
{
result << "1000";
index = UnitPrefIndexMax;
}
else
{
result << '1';
}
}
else
{
result.precision( prec );
result << value;
}
else
{
result << value;
}
if( index != 0 )
result << UnitPrefixes[ index - UnitPrefIndexShift ];
return result.str();
}
// test case number width
const int TCNumWidth = 3;
class TestClass
{
int failed;
int count;
public:
TestClass():failed(0),count(0)
{}
void operator()( const string& val, const char* expected )
{
if( val == expected )
{
cout << "Test nr " << setw( TCNumWidth ) << setfill( '0' ) << ++count << " Passed\n";
}
else
{
++failed;
cerr << "Test nr " << setw( TCNumWidth ) << setfill( '0' ) << ++count << " FAILED!!!\n" ;
cerr << "\tFunction result is: " << val << endl;
cerr << "\tExpected result is: " << expected << endl;
}
}
~TestClass()
{
if( failed == 0 )
{
cout << "Congratulation all " << setw( TCNumWidth ) << setfill( '0' ) << cout;
cout << " test cases has passed!\n";
}
else
{
cerr << "Sorry, " << setw( TCNumWidth ) << setfill( '0' ) << failed;
cerr << " of " << setw( TCNumWidth ) << setfill( '0' ) << count;
cerr << " test cases has FAILLED!\n";
cout << "Only ";
cout << setw( TCNumWidth ) << setfill( '0' ) << (count - failed) << " of " << count;
cout << " test cases has passed.\n";
}
}
};
int main(void)
{
TestClass test;
cout << "Starting testing." << endl;
cout << "\nTesting no unit prefix:\n";
test( ToEngineer( 1.0 ), "1" ); // test case 1
test( ToEngineer( 9.99 ), "9.99" );
test( ToEngineer( 5.647 ), "5.65" );
test( ToEngineer( 564.7 ), "565" );
test( ToEngineer( 0.9997 ), "1" ); // test case 5
cout << "\nTesting kilo unit prefix:\n";
test( ToEngineer( 1.0e3 ), "1k" ); // test case 6
test( ToEngineer( 9.99e3 ), "9.99k" );
test( ToEngineer( 5.647e3 ), "5.65k" );
test( ToEngineer( 564.7e3 ), "565k" );
test( ToEngineer( 0.9997e3 ), "1k" ); // test case 10
cout << "\nTesting tera prefix:\n";
test( ToEngineer( 1.0e12 ), "1T" ); // test case 11
test( ToEngineer( 9.99e12 ), "9.99T" );
test( ToEngineer( 5.647e12 ), "5.65T" );
test( ToEngineer( 564.7e12 ), "565T" );
test( ToEngineer( 0.9997e12 ), "1T" ); // test case 15
cout << "\nTesting jotta unit prefix:\n";
test( ToEngineer( 1.0e24 ), "1Y" ); // test case 16
test( ToEngineer( 9.99e24 ), "9.99Y" );
test( ToEngineer( 5.647e24 ), "5.65Y" );
test( ToEngineer( 564.7e24 ), "565Y" );
test( ToEngineer( 0.9997e24 ), "1Y" ); // test case 20
cout << "\nTesting beyond jotta unit prefix:\n";
test( ToEngineer( 1.0e27 ), "1000Y" );
test( ToEngineer( 9.99e27 ), "9990Y" );
test( ToEngineer( 5.647e27 ), "5650Y" );
test( ToEngineer( 564.7e27 ), "565000Y" );
test( ToEngineer( 0.9997e27 ), "1000Y" ); // test case 25
cout << "\nTesting mili unit prefix:\n";
test( ToEngineer( 1.0e-3 ), "1m" );
test( ToEngineer( 9.99e-3 ), "9.99m" );
test( ToEngineer( 5.647e-3 ), "5.65m" );
test( ToEngineer( 564.7e-3 ), "565m" );
test( ToEngineer( 0.9997e-3 ), "1m" ); // test case 30
cout << "\nTesting piko prefix:\n";
test( ToEngineer( 1.0e-12 ), "1p" );
test( ToEngineer( 9.99e-12 ), "9.99p" );
test( ToEngineer( 5.647e-12 ), "5.65p" );
test( ToEngineer( 564.7e-12 ), "565p" );
test( ToEngineer( 0.9997e-12 ), "1p" ); // test case 35
cout << "\nTesting jokto unit prefix:\n";
test( ToEngineer( 1.0e-24 ), "1y" );
test( ToEngineer( 9.99e-24 ), "9.99y" );
test( ToEngineer( 5.647e-24 ), "5.65y" );
test( ToEngineer( 564.7e-24 ), "565y" );
test( ToEngineer( 0.9997e-24 ), "1y" ); // test case 40
cout << "\nTesting beyond jokto unit prefix:\n";
test( ToEngineer( 1.0e-27 ), "0.001y" );
test( ToEngineer( 9.99e-27 ), "0.00999y" );
test( ToEngineer( 5.647e-27 ), "0.00565y" );
test( ToEngineer( 564.7e-27 ), "0.565y" );
test( ToEngineer( 0.9997e-27 ), "0.001y" ); // test case 45
cout << "\nSimple testing of remaining unit prefixes\n";
test( ToEngineer( 1.0e-21 ), "1z" ); // test case 46
test( ToEngineer( 1.0e-18 ), "1a" );
test( ToEngineer( 1.0e-15 ), "1f" );
// test( ToEngineer( 1.0e-12 ), "1p" ); // already tested
test( ToEngineer( 1.0e-9 ), "1n" );
test( ToEngineer( 1.0e-6 ), "1u" );
// test( ToEngineer( 1.0e-3 ), "1m" ); // already tested
// test( ToEngineer( 1.0 ), "1" ); // already tested
// test( ToEngineer( 1.0e3 ), "1k" ); // already tested
test( ToEngineer( 1.0e6 ), "1M" );
test( ToEngineer( 1.0e9 ), "1G" );
// test( ToEngineer( 1.0e12 ), "1T" ); // already tested
test( ToEngineer( 1.0e15 ), "1P" );
test( ToEngineer( 1.0e18 ), "1E" );
test( ToEngineer( 1.0e21 ), "1Z" );
cout << "\n\nSet of tests for negative numbers\n";
cout << "\nTesting no unit prefix:\n";
test( ToEngineer( -1.0 ), "-1" ); // test case 56
test( ToEngineer( -9.99 ), "-9.99" );
test( ToEngineer( -5.647 ), "-5.65" );
test( ToEngineer( -564.7 ), "-565" );
test( ToEngineer( -0.9997 ), "-1" ); // test case 60
cout << "\nTesting kilo unit prefix:\n";
test( ToEngineer( -1.0e3 ), "-1k" ); // test case 61
test( ToEngineer( -9.99e3 ), "-9.99k" );
test( ToEngineer( -5.647e3 ), "-5.65k" );
test( ToEngineer( -564.7e3 ), "-565k" );
test( ToEngineer( -0.9997e3 ), "-1k" ); // test case 65
cout << "\nTesting tera prefix:\n";
test( ToEngineer( -1.0e12 ), "-1T" ); // test case 66
test( ToEngineer( -9.99e12 ), "-9.99T" );
test( ToEngineer( -5.647e12 ), "-5.65T" );
test( ToEngineer( -564.7e12 ), "-565T" );
test( ToEngineer( -0.9997e12 ), "-1T" ); // test case 70
cout << "\nTesting jotta unit prefix:\n";
test( ToEngineer( -1.0e24 ), "-1Y" ); // test case 71
test( ToEngineer( -9.99e24 ), "-9.99Y" );
test( ToEngineer( -5.647e24 ), "-5.65Y" );
test( ToEngineer( -564.7e24 ), "-565Y" );
test( ToEngineer( -0.9997e24 ), "-1Y" ); // test case 75
cout << "\nTesting beyond jotta unit prefix:\n";
test( ToEngineer( -1.0e27 ), "-1000Y" );
test( ToEngineer( -9.99e27 ), "-9990Y" );
test( ToEngineer( -5.647e27 ), "-5650Y" );
test( ToEngineer( -564.7e27 ), "-565000Y" );
test( ToEngineer( -0.9997e27 ), "-1000Y" ); // test case 80
cout << "\nTesting mili unit prefix:\n";
test( ToEngineer( -1.0e-3 ), "-1m" );
test( ToEngineer( -9.99e-3 ), "-9.99m" );
test( ToEngineer( -5.647e-3 ), "-5.65m" );
test( ToEngineer( -564.7e-3 ), "-565m" );
test( ToEngineer( -0.9997e-3 ), "-1m" ); // test case 85
cout << "\nTesting piko prefix:\n";
test( ToEngineer( -1.0e-12 ), "-1p" );
test( ToEngineer( -9.99e-12 ), "-9.99p" );
test( ToEngineer( -5.647e-12 ), "-5.65p" );
test( ToEngineer( -564.7e-12 ), "-565p" );
test( ToEngineer( -0.9997e-12 ), "-1p" ); // test case 90
cout << "\nTesting jokto unit prefix:\n";
test( ToEngineer( -1.0e-24 ), "-1y" );
test( ToEngineer( -9.99e-24 ), "-9.99y" );
test( ToEngineer( -5.647e-24 ), "-5.65y" );
test( ToEngineer( -564.7e-24 ), "-565y" );
test( ToEngineer( -0.9997e-24 ), "-1y" ); // test case 95
cout << "\nTesting beyond jokto unit prefix:\n";
test( ToEngineer( -1.0e-27 ), "-0.001y" );
test( ToEngineer( -9.99e-27 ), "-0.00999y" );
test( ToEngineer( -5.647e-27 ), "-0.00565y" );
test( ToEngineer( -564.7e-27 ), "-0.565y" );
test( ToEngineer( -0.9997e-27 ), "-0.001y" ); // test case 100
cout << "\nSimple testing of remaining unit prefixes\n";
test( ToEngineer( -1.0e-21 ), "-1z" ); // test case 101
test( ToEngineer( -1.0e-18 ), "-1a" );
test( ToEngineer( -1.0e-15 ), "-1f" );
// test( ToEngineer( -1.0e-12 ), "-1p" ); // already tested
test( ToEngineer( -1.0e-9 ), "-1n" );
test( ToEngineer( -1.0e-6 ), "-1u" ); // test case 105
// test( ToEngineer( -1.0e-3 ), "-1m" ); // already tested
// test( ToEngineer( -1.0 ), "-1" ); // already tested
// test( ToEngineer( -1.0e3 ), "-1k" ); // already tested
test( ToEngineer( -1.0e6 ), "-1M" );
test( ToEngineer( -1.0e9 ), "-1G" );
// test( ToEngineer( -1.0e12 ), "-1T" ); // already tested
test( ToEngineer( -1.0e15 ), "-1P" );
test( ToEngineer( -1.0e18 ), "-1E" );
test( ToEngineer( -1.0e21 ), "-1Z" ); // 110
}
edit:
usunąłem jeden błąd teraz jest tylko problem zaokrągleń dla dużych liczb (4 teste case-y nie przechodzą), to jest błąd niewarty uwagi. Powodzenia.
Heh, faktycznie "parę linijek" :)
Dzięki bardzo za kod, postaram się go teraz dokładnie przeorać. Pozdrawiam.
Zasadniczo to tylko 57, a i tak napisałem to dość rozwlekle (w miarę przejrzyście).
Większość tych linijek to sprawdzanie czy ten kod działa tak jak powinien i jak zobaczysz to dla liczby 5.647e27 i podobnych zamiast dać zaokrąglone (precyzja 3 cyfr) "5650Y" to zwraca "5647Y". A problem pojawia się tylko dla liczb większych niż 1.0e27.
Kod działa bardzo fajnie, dla mnie takie duże liczby nie mają znaczenia. Operuję w zakresie mniej więcej od piko do mega, czyli typowe wartości R, L, C. Także pod to zmodyfikowałem trochę kod, który dodaje na koniec odpowiednią jednostkę.
Jeszcze raz dzięki.