1
\$\begingroup\$

Follow up of Pretty print with unit and physical suffix

My first code didn't process negative numbers correctly. Additionally I tried to use all of the helpful input to come up with this solution.

I considered:

  • precision
  • testing
  • spacing tips

P.S.: I use the 'u' instead of U+03BC because I had some difficulties displaying it on my Windows console.

# -*- coding: utf-8 -*-

import math

def eng_format(x, unit='', precision=3):
    UNITS = [' ', ' k', ' M', ' G'] + \
            ([None] * 10) + \
            [' f', ' p', ' n', ' u', ' m']

    power_of_1000 = int(math.floor(math.log10(abs(x)) // 3))
    exponent = 3 * power_of_1000
    prefix = UNITS[power_of_1000]
    if prefix is None:
        prefix = '*10^%d ' % exponent

    predecimal = 1+int(math.floor(math.log10(abs(x)) % 3))
    significand = x * 10**(-exponent + precision - predecimal)
    significand = round(significand) / 10**(precision - predecimal)

    postdecimal = precision - predecimal
    if(postdecimal < 0):
        postdecimal = 0
    FormatStr = '{:.%df}{:s}{:s}'%(postdecimal)
    return FormatStr.format(significand, prefix, unit)

if __name__ == '__main__':
    import unittest

    class TestPrettyPrinting(unittest.TestCase):
        def setUp(self):
            self.seq = list()
            self.seq.append( (0.002777, 'V', 3, '2.78 mV') )
            self.seq.append( (0.02777, 'V', 3, '27.8 mV') )
            self.seq.append( (0.2777, 'V', 3, '278 mV') )
            self.seq.append( (2.777, 'V', 3, '2.78 V') )
            self.seq.append( (27.77, 'V', 3, '27.8 V') )
            self.seq.append( (2./3, 'V', 6, '666.667 mV') )
            self.seq.append( (0.000003221, 'A', 3, '3.22 uA') )
            self.seq.append( (30589, 'A', 4, '30.59 kA') )
            self.seq.append( (60000000, 'W', 6, '60.0000 MW') )
            self.seq.append( (6000000000000000, 'W', 1, '6*10^15 W') )
            self.seq.append( (.0000000000000006, 'W', 4, '600.0*10^-18 W') )

        def test_eng_format(self):
            for value, unit, precission, expected_result in self.seq:
                for si, s in enumerate(['', '-']):
                    new_expected_result = s + expected_result
                    result = eng_format(value * (-1)**si, unit, precission)
                    print "%20s %20s"%(result, new_expected_result)
                    self.assertEqual(result, new_expected_result)

    unittest.main()
\$\endgroup\$
3
\$\begingroup\$

The computation of postdecimal can be simplified to postdecimal = max(precision - predecimal, 0).

eng_format needs a docstring.

On self.seq:

  • It needs a meaningful name like examples.
  • It should be local to test_eng_format, since that's the only place it's used.
  • It can be built in one expression, without mutation:

    examples = [(0.002777, 'V', 3, '2.78 mV'),
                (0.02777, 'V', 3, '27.8 mV'),
                ...]
    
  • However, looping over a list of test cases is less readable than a bunch of self.assertEqual(eng_format(-0.1, 'A', 6), '-100.000 mA') calls.

test_eng_format should not print all its test cases. Tests should be silent unless they fail.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.