Zapis liczb, postać inżynierska, konwersja

0

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.

0

Watpie, ale to raptem pare linijek, nie szybciej samemu napisac?

0

do takich błahostek bibliotek się nie robi

0

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.

0

Heh, faktycznie "parę linijek" :)

Dzięki bardzo za kod, postaram się go teraz dokładnie przeorać. Pozdrawiam.

0

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.

0

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.

1 użytkowników online, w tym zalogowanych: 0, gości: 1