├── .github └── workflows │ └── unittest.yml ├── .gitignore ├── engineering_notation ├── __init__.py ├── engineering_notation.py └── version.py ├── license.txt ├── readme.md ├── setup.py └── tests └── test_engnum.py /.github/workflows/unittest.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: push 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] 9 | 10 | timeout-minutes: 10 11 | 12 | steps: 13 | - name: Check out repo 14 | uses: actions/checkout@v2 15 | 16 | - name: Setup Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | 21 | - name: Install Requirements 22 | run: python -m pip install pytest coverage pytest-cov 23 | 24 | - name: Install package 25 | run: python setup.py install 26 | 27 | - name: Execute Tests 28 | run: flake8 engineering_notation & pytest -v tests 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .cache/ 3 | *.egg-info/ 4 | .eggs/ 5 | __pycache__/ 6 | build/ 7 | dist/ 8 | venv/ 9 | .mypy_cache 10 | .venv 11 | conftest.py 12 | .pytest_cache 13 | .vscode 14 | setup.cfg 15 | .coverage -------------------------------------------------------------------------------- /engineering_notation/__init__.py: -------------------------------------------------------------------------------- 1 | from engineering_notation.engineering_notation import EngNumber, EngUnit 2 | from engineering_notation.version import __version__ 3 | 4 | __all__ = ['EngNumber', 'EngUnit', '__version__'] 5 | -------------------------------------------------------------------------------- /engineering_notation/engineering_notation.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from string import digits 3 | import sys 4 | 5 | from typing import Optional 6 | 7 | try: 8 | import numpy 9 | except ImportError: 10 | pass 11 | 12 | _suffix_lookup = { 13 | 'y': 'e-24', 14 | 'z': 'e-21', 15 | 'a': 'e-18', 16 | 'f': 'e-15', 17 | 'p': 'e-12', 18 | 'n': 'e-9', 19 | 'u': 'e-6', 20 | 'm': 'e-3', 21 | '': 'e0', 22 | 'k': 'e3', 23 | 'M': 'e6', 24 | 'G': 'e9', 25 | 'T': 'e12', 26 | 'P': 'e15', 27 | 'E': 'e18', 28 | 'Z': 'e21', 29 | } 30 | 31 | _exponent_lookup_scaled = { 32 | '-54': 'y', 33 | '-51': 'z', 34 | '-48': 'a', 35 | '-45': 'f', 36 | '-42': 'p', 37 | '-39': 'n', 38 | '-36': 'u', 39 | '-33': 'm', 40 | '-30': '', 41 | '-27': 'k', 42 | '-24': 'M', 43 | '-21': 'G', 44 | '-18': 'T', 45 | '-15': 'P', 46 | '-12': 'E', 47 | '-9': 'Z', 48 | } 49 | 50 | 51 | class EngUnit: 52 | """ 53 | Represents an engineering number, complete with units 54 | """ 55 | def __init__(self, value, 56 | precision=2, significant=0, unit: Optional[str] = None, separator=""): 57 | """ 58 | Initialize engineering with units 59 | :param value: the desired value in the form of a string, int, or float 60 | :param precision: the number of decimal places 61 | :param significant: the number of significant digits 62 | if given, significant takes precendence over precision 63 | """ 64 | suffix_keys = [key for key in _suffix_lookup.keys() if key != ''] 65 | self.unit = unit 66 | 67 | if isinstance(value, str): 68 | # parse the string into unit and engineering number 69 | new_value = '' 70 | v_index = 0 71 | for c in value: 72 | if (c in digits) or (c in ['.', '-']) or (c in suffix_keys): 73 | new_value += c 74 | v_index += 1 75 | else: 76 | break 77 | 78 | if self.unit is None and len(value) >= v_index: 79 | self.unit = value[v_index:] 80 | 81 | self.eng_num = EngNumber(new_value, precision, 82 | significant, separator) 83 | 84 | else: 85 | self.eng_num = EngNumber(value, precision, significant, separator) 86 | 87 | def __repr__(self): 88 | """ 89 | Returns the object representation 90 | :return: a string representing the engineering number 91 | """ 92 | unit = self.unit if self.unit else '' 93 | return str(self.eng_num) + unit 94 | 95 | def __str__(self): 96 | """ 97 | Returns the string representation 98 | :return: a string representing the engineering number 99 | """ 100 | return self.__repr__() 101 | 102 | def __int__(self): 103 | """ 104 | Implements the 'int()' method 105 | :return: 106 | """ 107 | return int(self.eng_num) 108 | 109 | def __float__(self): 110 | """ 111 | Implements the 'float()' method 112 | :return: 113 | """ 114 | return float(self.eng_num) 115 | 116 | def __add__(self, other): 117 | """ 118 | Add two engineering numbers, with units 119 | :param other: EngNum, str, float, or int 120 | :return: result 121 | """ 122 | if not isinstance(other, EngNumber): 123 | other = EngUnit(str(other)) 124 | 125 | if self.unit != other.unit: 126 | raise AttributeError('units do not match') 127 | 128 | return EngUnit(str(self.eng_num + other.eng_num) + self.unit) 129 | 130 | def __radd__(self, other): 131 | """ 132 | Add two engineering numbers, with units 133 | :param other: EngNum, str, float, or int 134 | :return: result 135 | """ 136 | return self.__add__(other) 137 | 138 | def __sub__(self, other): 139 | """ 140 | Subtract two engineering numbers, with units 141 | :param other: EngNum, str, float, or int 142 | :return: result 143 | """ 144 | if not isinstance(other, EngNumber): 145 | other = EngUnit(str(other)) 146 | 147 | if self.unit != other.unit: 148 | raise AttributeError('units do not match') 149 | 150 | return EngUnit(str(self.eng_num - other.eng_num) + self.unit) 151 | 152 | def __rsub__(self, other): 153 | """ 154 | Subtract two engineering numbers, with units 155 | :param other: EngNum, str, float, or int 156 | :return: result 157 | """ 158 | if not isinstance(other, EngNumber): 159 | other = EngUnit(str(other)) 160 | 161 | if self.unit != other.unit: 162 | raise AttributeError('units do not match') 163 | 164 | return EngUnit(str(other.eng_num - self.eng_num) + self.unit) 165 | 166 | def __mul__(self, other): 167 | """ 168 | Multiply two engineering numbers, with units 169 | :param other: EngNum, str, float, or int 170 | :return: result 171 | """ 172 | if not isinstance(other, EngNumber): 173 | other = EngUnit(str(other)) 174 | 175 | return EngUnit(str(self.eng_num * other.eng_num) 176 | + self.unit + other.unit) 177 | 178 | def __rmul__(self, other): 179 | """ 180 | Multiply two engineering numbers, with units 181 | :param other: EngNum, str, float, or int 182 | :return: result 183 | """ 184 | return self.__mul__(other) 185 | 186 | def __truediv__(self, other): 187 | """ 188 | Divide two engineering numbers, with units 189 | :param other: EngNum, str, float, or int 190 | :return: result 191 | """ 192 | if not isinstance(other, EngNumber): 193 | other = EngUnit(str(other)) 194 | 195 | new_unit = '' 196 | if self.unit: 197 | new_unit += self.unit 198 | if other.unit: 199 | new_unit += '/' + other.unit 200 | 201 | return EngUnit(str(self.eng_num / other.eng_num) + new_unit) 202 | 203 | def __rtruediv__(self, other): 204 | """ 205 | Divide two engineering numbers, with units 206 | :param other: EngNum, str, float, or int 207 | :return: result 208 | """ 209 | if not isinstance(other, EngNumber): 210 | other = EngUnit(str(other)) 211 | 212 | return EngUnit(str(other.eng_num / self.eng_num) 213 | + (other.unit + '/' + self.unit)) 214 | 215 | def __lt__(self, other): 216 | """ 217 | Compare two engineering numbers, with units 218 | :param other: EngNum, str, float, or int 219 | :return: result 220 | """ 221 | if not isinstance(other, EngNumber): 222 | other = EngUnit(str(other)) 223 | 224 | if self.unit != other.unit: 225 | raise AttributeError('units do not match') 226 | 227 | return self.eng_num < other.eng_num 228 | 229 | def __gt__(self, other): 230 | """ 231 | Compare two engineering numbers, with units 232 | :param other: EngNum, str, float, or int 233 | :return: result 234 | """ 235 | if not isinstance(other, EngNumber): 236 | other = EngUnit(str(other)) 237 | 238 | if self.unit != other.unit: 239 | raise AttributeError('units do not match') 240 | 241 | return self.eng_num > other.eng_num 242 | 243 | def __le__(self, other): 244 | """ 245 | Compare two engineering numbers, with units 246 | :param other: EngNum, str, float, or int 247 | :return: result 248 | """ 249 | if not isinstance(other, EngNumber): 250 | other = EngUnit(str(other)) 251 | 252 | if self.unit != other.unit: 253 | raise AttributeError('units do not match') 254 | 255 | return self.eng_num <= other.eng_num 256 | 257 | def __ge__(self, other): 258 | """ 259 | Compare two engineering numbers, with units 260 | :param other: EngNum, str, float, or int 261 | :return: result 262 | """ 263 | if not isinstance(other, EngNumber): 264 | other = EngUnit(str(other)) 265 | 266 | if self.unit != other.unit: 267 | raise AttributeError('units do not match') 268 | 269 | return self.eng_num >= other.eng_num 270 | 271 | def __eq__(self, other): 272 | """ 273 | Compare two engineering numbers, with units 274 | :param other: EngNum, str, float, or int 275 | :return: result 276 | """ 277 | if not isinstance(other, (EngNumber, EngUnit, str, int, float)): 278 | return NotImplemented 279 | if not isinstance(other, EngNumber): 280 | other = EngUnit(str(other)) 281 | 282 | if self.unit != other.unit: 283 | raise AttributeError('units do not match') 284 | 285 | return self.eng_num == other.eng_num 286 | 287 | 288 | class EngNumber: 289 | """ 290 | Used for easy manipulation of numbers which use engineering notation 291 | """ 292 | 293 | def __init__(self, value, 294 | precision=2, significant=0, separator=""): 295 | """ 296 | Initialize the class 297 | 298 | :param value: string, integer, or float representing 299 | the numeric value of the number 300 | :param precision: the precision past the decimal - default to 2 301 | :param significant: the number of significant digits 302 | if given, significant takes precendence over precision 303 | """ 304 | self.precision = precision 305 | self.significant = significant 306 | self.separator = separator 307 | 308 | if isinstance(value, str): 309 | suffix_keys = [key for key in _suffix_lookup.keys() if key != ''] 310 | 311 | for suffix in suffix_keys: 312 | if suffix == value[-1]: 313 | value = value[:-1] + _suffix_lookup[suffix] 314 | break 315 | 316 | self.number = Decimal(value) 317 | 318 | elif (isinstance(value, int) 319 | or isinstance(value, float) 320 | or isinstance(value, EngNumber)): 321 | self.number = Decimal(str(value)) 322 | else: 323 | # finally, check for numpy import 324 | if 'numpy' in sys.modules and isinstance(value, numpy.integer): 325 | self.number = Decimal(str(value)) 326 | 327 | def to_pn(self, sub_letter=None): 328 | """ 329 | Returns the part number equivalent. For instance, 330 | a '1k' would still be '1k', but a 331 | '1.2k' would, instead, be a '1k2' 332 | :return: 333 | """ 334 | string = str(self) 335 | if '.' not in string: 336 | return string 337 | 338 | # take care of the case of when there is no scaling unit 339 | if not string[-1].isalpha(): 340 | if sub_letter is not None: 341 | return string.replace('.', sub_letter) 342 | 343 | return string 344 | 345 | letter = string[-1] 346 | return string.replace('.', letter)[:-1].strip(self.separator) 347 | 348 | def __repr__(self): 349 | """ 350 | Returns the string representation 351 | :return: a string representing the engineering number 352 | """ 353 | # since Decimal class only really converts number that are very small 354 | # into engineering notation, then we will simply make all number a 355 | # small number and take advantage of Decimal class 356 | num_str = self.number * Decimal('10e-31') 357 | num_str = num_str.to_eng_string().lower() 358 | 359 | base, exponent = num_str.split('e') 360 | 361 | if self.significant > 0: 362 | if abs(Decimal(base)) >= 100.0: 363 | base = str(round(Decimal(base), self.significant - 3)) 364 | elif abs(Decimal(base)) >= 10.0: 365 | base = str(round(Decimal(base), self.significant - 2)) 366 | else: 367 | base = str(round(Decimal(base), self.significant - 1)) 368 | else: 369 | base = str(round(Decimal(base), self.precision)) 370 | 371 | if 'e' in base.lower(): 372 | base = str(int(Decimal(base))) 373 | 374 | # remove trailing decimals: 375 | # print(base) 376 | # https://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python 377 | # https://stackoverflow.com/questions/11227620/drop-trailing-zeros-from-decimal 378 | # base = '%s' % float("%#.2G"%Decimal(base)) 379 | # print(base) 380 | # remove trailing decimal 381 | if '.' in base: 382 | base = base.rstrip('.') 383 | 384 | # remove trailing .00 in precision 2 385 | if self.precision == 2 and self.significant == 0: 386 | if '.00' in base: 387 | base = base[:-3] 388 | 389 | return base + self.separator + _exponent_lookup_scaled[exponent] 390 | 391 | def __str__(self, eng=True, context=None): 392 | """ 393 | Returns the string representation 394 | :return: a string representing the engineering number 395 | """ 396 | return self.__repr__() 397 | 398 | def __int__(self): 399 | """ 400 | Implements the 'int()' method 401 | :return: 402 | """ 403 | return int(self.number) 404 | 405 | def __float__(self): 406 | """ 407 | Implements the 'float()' method 408 | :return: 409 | """ 410 | return float(self.number) 411 | 412 | def __add__(self, other): 413 | """ 414 | Add two engineering numbers 415 | :param other: EngNum, str, float, or int 416 | :return: result 417 | """ 418 | if not isinstance(other, EngNumber): 419 | other = EngNumber(other) 420 | 421 | num = self.number + other.number 422 | return EngNumber(str(num)) 423 | 424 | def __radd__(self, other): 425 | """ 426 | Add two engineering numbers 427 | :param other: EngNum, str, float, or int 428 | :return: result 429 | """ 430 | return self.__add__(other) 431 | 432 | def __sub__(self, other): 433 | """ 434 | Subtract two engineering numbers 435 | :param other: EngNum, str, float, or int 436 | :return: result 437 | """ 438 | if not isinstance(other, EngNumber): 439 | other = EngNumber(other) 440 | 441 | num = self.number - other.number 442 | return EngNumber(str(num)) 443 | 444 | def __rsub__(self, other): 445 | """ 446 | Subtract two engineering numbers 447 | :param other: EngNum, str, float, or int 448 | :return: result 449 | """ 450 | if not isinstance(other, EngNumber): 451 | other = EngNumber(other) 452 | 453 | num = other.number - self.number 454 | return EngNumber(str(num)) 455 | 456 | def __mul__(self, other): 457 | """ 458 | Multiply two engineering numbers 459 | :param other: EngNum, str, float, or int 460 | :return: result 461 | """ 462 | if not isinstance(other, EngNumber): 463 | other = EngNumber(other) 464 | 465 | num = self.number * other.number 466 | return EngNumber(str(num)) 467 | 468 | def __rmul__(self, other): 469 | """ 470 | Multiply two engineering numbers 471 | :param other: EngNum, str, float, or int 472 | :return: result 473 | """ 474 | return self.__mul__(other) 475 | 476 | def __truediv__(self, other): 477 | """ 478 | Divide two engineering numbers 479 | :param other: EngNum, str, float, or int 480 | :return: result 481 | """ 482 | if not isinstance(other, EngNumber): 483 | other = EngNumber(other) 484 | 485 | num = self.number / other.number 486 | return EngNumber(str(num)) 487 | 488 | def __rtruediv__(self, other): 489 | """ 490 | Divide two engineering numbers 491 | :param other: EngNum, str, float, or int 492 | :return: result 493 | """ 494 | if not isinstance(other, EngNumber): 495 | other = EngNumber(other) 496 | 497 | num = other.number / self.number 498 | return EngNumber(str(num)) 499 | 500 | def __lt__(self, other): 501 | """ 502 | Compare two engineering numbers 503 | :param other: EngNum, str, float, or int 504 | :return: result 505 | """ 506 | if not isinstance(other, EngNumber): 507 | other = EngNumber(other) 508 | 509 | return self.number < other.number 510 | 511 | def __gt__(self, other): 512 | """ 513 | Compare two engineering numbers 514 | :param other: EngNum, str, float, or int 515 | :return: result 516 | """ 517 | if not isinstance(other, EngNumber): 518 | other = EngNumber(other) 519 | 520 | return self.number > other.number 521 | 522 | def __le__(self, other): 523 | """ 524 | Compare two engineering numbers 525 | :param other: EngNum, str, float, or int 526 | :return: result 527 | """ 528 | if not isinstance(other, EngNumber): 529 | other = EngNumber(other) 530 | 531 | return self.number <= other.number 532 | 533 | def __ge__(self, other): 534 | """ 535 | Compare two engineering numbers 536 | :param other: EngNum, str, float, or int 537 | :return: result 538 | """ 539 | if not isinstance(other, EngNumber): 540 | other = EngNumber(other) 541 | 542 | return self.number >= other.number 543 | 544 | def __eq__(self, other): 545 | """ 546 | Compare two engineering numbers 547 | :param other: EngNum, str, float, or int 548 | :return: result 549 | """ 550 | if not isinstance(other, (EngNumber, str, int, float)): 551 | return NotImplemented 552 | if not isinstance(other, EngNumber): 553 | other = EngNumber(other) 554 | 555 | return self.number == other.number 556 | -------------------------------------------------------------------------------- /engineering_notation/version.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.11.0' 2 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jason R. Jones 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 6 | persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Unit Tests](https://github.com/slightlynybbled/engineering_notation/actions/workflows/unittest.yml/badge.svg)](https://github.com/slightlynybbled/engineering_notation/actions/workflows/unittest.yml) 2 | 3 | # Purpose 4 | 5 | To easily work with human-readable engineering notation. I wrote this as a quick tool for my own use. 6 | I found that I was writing the same functionality into multiple packages and would like a quick pip-installable 7 | package to take care of this manipulation for me. The package should be easily extended for other use cases. 8 | The package is unit-less, so only operates on numeric values. Unit detection may be added in future versions. 9 | 10 | More information may be found at [for(embed)](http://forembed.com/engineering-notation-in-python.html). 11 | 12 | # Installation 13 | 14 | Install using pip: `pip install engineering_notation`. 15 | 16 | # Status and Contributions 17 | 18 | This project currently has 100% test coverage. Have a look in `test.py` for examples of how to use 19 | this library. To execute the tests, run `pytest` from the main directory or, 20 | in some environments, it may be necessary to run `python3 -m pytest`. 21 | 22 | Any contributions must pass 100% of current tests and pass flake8. To execute 23 | flake8, navigate to the project directory and `python3 setup.py flake8`. 24 | 25 | Your pull request will automatically be run through testing and flake8 checks and 26 | any pull requests that do not pass these will be put on hold pending passing. 27 | 28 | # Use 29 | 30 | There are multiple ways of initializing a number to a particular value, but a string is the preferred method: 31 | 32 | ``` 33 | >>> from engineering_notation import EngNumber 34 | >>> EngNumber('10k') 35 | 10k 36 | >>> EngNumber('10000') 37 | 10k 38 | >>> EngNumber(10000) 39 | 10k 40 | >>> EngNumber(10000.0) 41 | 10k 42 | >>> EngNumber(1e4) 43 | 10k 44 | ``` 45 | 46 | Where decimals are involved, we use a default precision of 2 digits: 47 | 48 | ``` 49 | >>> EngNumber('4.99k') 50 | 4.99k 51 | >>> EngNumber('4.9k') 52 | 4.90k 53 | ``` 54 | 55 | This behavior can truncate your results in some cases, and cause your number to round. To specify more or less 56 | digits, simply specify the precision in the declaration: 57 | 58 | ``` 59 | >>> EngNumber('4.999k') 60 | 5k 61 | >>> EngNumber('4.999k', precision=3) 62 | 4.999k 63 | ``` 64 | 65 | Most operations that you would perform on numeric values are valid, although all operations are not implemented: 66 | 67 | ``` 68 | >>> EngNumber('2.2k') * 2 69 | 4.40k 70 | >>> 2 * EngNumber('2.2k') 71 | 4.40k 72 | >>> EngNumber(1.2) > EngNumber('3.3k') 73 | False 74 | >>> EngNumber(1.2) <= EngNumber('3.3k') 75 | True 76 | >>> EngNumber('3.3k') == EngNumber(3300) 77 | True 78 | ``` 79 | 80 | All of the above operations are also possible on the `EngUnit()` class as well. The only difference is 81 | that units must match for addition/subtraction/comparison operations. Although multiplication and division 82 | operations will work numerically, they may not always be strictly correct. This is because EngUnit is not 83 | intended to replace a computer algebra system! 84 | 85 | ``` 86 | >>> EngUnit('2s') / EngUnit('4rotations') 87 | 0.5s/rotations 88 | ``` 89 | 90 | Additionally, since there are 'reserved' letters for sizing the number, you must be careful with your units! 91 | 92 | ``` 93 | >>> EngUnit('2mm') 94 | 2mm # <<< this value equivalent to "0.002m" 95 | >>> EngUnit('2meter') 96 | 2meter # <<< this value is equivalent to "0.002eter", the "m" was used to scale the unit! 97 | >>> EngUnit('2', unit='meter') # <<< this will work better 98 | ``` 99 | 100 | # Contributions 101 | 102 | Contributions are welcome. Feel free to make feature requests in the issues. 103 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | import os 4 | 5 | __version__ = None 6 | 7 | # --------------------------------- 8 | # imports the version from the package (with a pycharm patch) 9 | here = os.path.dirname(os.path.dirname(__file__)) 10 | try: 11 | with open(os.path.join(here, 'engineering_notation/version.py'), 'r') as f: 12 | fdata = f.read() 13 | except FileNotFoundError: 14 | with open(os.path.join(here, 'engineering_notation/engineering_notation/version.py'), 'r') as f: 15 | fdata = f.read() 16 | 17 | exec(fdata) 18 | 19 | try: 20 | with open('readme.md', 'r') as f: 21 | readme = f.read() 22 | except FileNotFoundError: 23 | readme = '' 24 | 25 | # --------------------------------- 26 | # project requirements 27 | requirements = [] 28 | 29 | # --------------------------------- 30 | # project setup 31 | setup( 32 | name='engineering_notation', 33 | version=__version__, 34 | description='Easy engineering notation', 35 | long_description=readme, 36 | long_description_content_type='text/markdown', 37 | author='Jason R. Jones', 38 | author_email='slightlynybbled@gmail.com', 39 | url='https://github.com/slightlynybbled/engineering_notation', 40 | packages=["engineering_notation"], 41 | package_data={"engineering_notation": ["py.typed"]}, 42 | install_requires=requirements, 43 | setup_requires=['flake8', 'pytest'], 44 | license='MIT', 45 | classifiers=[ 46 | 'Development Status :: 4 - Beta', 47 | 'Programming Language :: Python :: 3', 48 | 'Programming Language :: Python :: 3.5', 49 | 'Programming Language :: Python :: 3.6', 50 | 'Natural Language :: English' 51 | ], 52 | keywords='engineering notation decimal' 53 | ) 54 | -------------------------------------------------------------------------------- /tests/test_engnum.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from engineering_notation import EngNumber, EngUnit, __version__ 3 | 4 | 5 | def test_import_version(): 6 | assert __version__ 7 | assert len(__version__.split('.')) == 3 8 | 9 | 10 | ''' tests for EngNum ''' 11 | 12 | 13 | def test_enum_to_str_large(): 14 | # positive_numbers 15 | assert str(EngNumber('220k')) == '220k' 16 | assert str(EngNumber('220000')) == '220k' 17 | assert str(EngNumber(220000)) == '220k' 18 | assert str(EngNumber(220000.00)) == '220k' 19 | assert str(EngNumber(220001.25)) == '220k' 20 | 21 | # negative_numbers 22 | assert str(EngNumber('-220k')) == '-220k' 23 | assert str(EngNumber('-220000')) == '-220k' 24 | assert str(EngNumber(-220000)) == '-220k' 25 | assert str(EngNumber(-220000.00)) == '-220k' 26 | assert str(EngNumber(-220001.25)) == '-220k' 27 | 28 | 29 | def test_engum_to_str_small(): 30 | # positive_numbers 31 | assert str(EngNumber('220m')) == '220m' 32 | assert str(EngNumber('0.220')) == '220m' 33 | assert str(EngNumber(0.220)) == '220m' 34 | assert str(EngNumber(0.220000125)) == '220m' 35 | 36 | # negative_numbers 37 | assert str(EngNumber('-220m')) == '-220m' 38 | assert str(EngNumber('-0.220')) == '-220m' 39 | assert str(EngNumber(-0.220)) == '-220m' 40 | assert str(EngNumber(-0.220000125)) == '-220m' 41 | 42 | 43 | def test_1000f(): 44 | assert str(EngNumber('1000f')) == '1p' 45 | assert str(EngNumber('1p')) == '1p' 46 | assert str(EngNumber(0.000000000001)) == '1p' 47 | 48 | 49 | def test_engnum_significant(): 50 | assert str(EngNumber('220m', significant=0)) == '220m' 51 | assert str(EngNumber('220m', significant=1)) == '200m' 52 | assert str(EngNumber('220m', significant=2)) == '220m' 53 | assert str(EngNumber('220m', significant=3)) == '220m' 54 | assert str(EngNumber('220m', significant=4)) == '220.0m' 55 | assert str(EngNumber('220m', significant=5)) == '220.00m' 56 | 57 | assert str(EngNumber('22m', significant=0)) == '22m' 58 | assert str(EngNumber('22m', significant=1)) == '20m' 59 | assert str(EngNumber('22m', significant=2)) == '22m' 60 | assert str(EngNumber('22m', significant=3)) == '22.0m' 61 | assert str(EngNumber('22.2m', significant=3)) == '22.2m' 62 | assert str(EngNumber('22m', significant=4)) == '22.00m' 63 | assert str(EngNumber('22.22m', significant=4)) == '22.22m' 64 | 65 | assert str(EngNumber('2m', significant=0)) == '2m' 66 | assert str(EngNumber('2m', significant=1)) == '2m' 67 | assert str(EngNumber('2m', significant=2)) == '2.0m' 68 | assert str(EngNumber('2.2m', significant=2)) == '2.2m' 69 | assert str(EngNumber('2.2m', significant=3)) == '2.20m' 70 | assert str(EngNumber('2.22m', significant=3)) == '2.22m' 71 | 72 | 73 | def test_engnum_separator(): 74 | assert str(EngNumber("1.23k", separator=" ")) == "1.23 k" 75 | assert str(EngNumber("1.23k", separator=" ").to_pn()) == "1k23" 76 | 77 | 78 | def test_new_units(): 79 | """ 80 | any new units - such as femto, atto, zepto, etc. should have 81 | some basic testing added here 82 | """ 83 | 84 | # testing femto 85 | assert str(EngNumber('220f')) == '220f' 86 | assert str(EngNumber(0.000000000000220)) == '220f' 87 | 88 | # testing atto 89 | assert str(EngNumber('220a')) == '220a' 90 | assert str(EngNumber(0.000000000000000220)) == '220a' 91 | 92 | # testing zepto 93 | assert str(EngNumber('220z')) == '220z' 94 | assert str(EngNumber(0.000000000000000000220)) == '220z' 95 | 96 | # testing yocto 97 | assert str(EngNumber('220y')) == '220y' 98 | assert str(EngNumber(0.000000000000000000000220)) == '220y' 99 | 100 | # testing peta 101 | assert str(EngNumber('220P')) == '220P' 102 | assert str(EngNumber(1e15)) == '1P' 103 | 104 | # testing exxa 105 | assert str(EngNumber('220E')) == '220E' 106 | assert str(EngNumber(1e18)) == '1E' 107 | 108 | # testing zetta 109 | assert str(EngNumber('220Z')) == '220Z' 110 | assert str(EngNumber(1e21)) == '1Z' 111 | 112 | # wrap it all up 113 | assert str(EngNumber('1f') + EngNumber('330a')) == '1.33f' 114 | assert str(EngNumber('3z') + EngNumber('440y')) == '3.44z' 115 | 116 | 117 | def test_enum_add(): 118 | # positive_numbers 119 | assert str(EngNumber('220m') + EngNumber('10m')) == '230m' 120 | assert str(EngNumber('220m') + 0.01) == '230m' 121 | assert str(0.01 + EngNumber('220m')) == '230m' 122 | 123 | assert str(EngNumber('220m') + EngNumber('220u')) == '220.22m' 124 | assert str(EngNumber('220m') + EngNumber('220n')) == '220m' 125 | 126 | # negative_numbers 127 | assert str(EngNumber('-220m') + EngNumber('-10m')) == '-230m' 128 | assert str(EngNumber('-220m') + -0.01) == '-230m' 129 | assert str(-0.01 + EngNumber('-220m')) == '-230m' 130 | 131 | assert str(EngNumber('-220m') + EngNumber('-220u')) == '-220.22m' 132 | assert str(EngNumber('-220m') + EngNumber('-220n')) == '-220m' 133 | 134 | 135 | def test_enum_sub(): 136 | # positive_numbers 137 | assert str(EngNumber('220m') - EngNumber('10m')) == '210m' 138 | assert str(EngNumber('220m') - 0.01) == '210m' 139 | 140 | assert str(EngNumber('220m') - EngNumber('220u')) == '219.78m' 141 | assert str(EngNumber('220m') - EngNumber('220n')) == '220m' 142 | 143 | assert str(0.220 - EngNumber('0.01')) == '210m' 144 | 145 | # negative_numbers 146 | assert str(EngNumber('-220m') - EngNumber('-10m')) == '-210m' 147 | assert str(EngNumber('-220m') - -0.01) == '-210m' 148 | 149 | assert str(EngNumber('-220m') - EngNumber('-220u')) == '-219.78m' 150 | assert str(EngNumber('-220m') - EngNumber('-220n')) == '-220m' 151 | 152 | assert str(-0.220 - EngNumber('-0.01')) == '-210m' 153 | 154 | 155 | def test_enum_mul(): 156 | # positive_numbers 157 | assert str(EngNumber('220m') * EngNumber('2')) == '440m' 158 | assert str(EngNumber('220m') * 2) == '440m' 159 | assert str(EngNumber('220m') * 2.0) == '440m' 160 | 161 | assert str(2 * EngNumber('220m')) == '440m' 162 | assert str(2.0 * EngNumber('220m')) == '440m' 163 | 164 | # negative_numbers 165 | assert str(EngNumber('-220m') * EngNumber('-2')) == '440m' 166 | assert str(EngNumber('-220m') * -2) == '440m' 167 | assert str(EngNumber('-220m') * -2.0) == '440m' 168 | 169 | assert str(-2 * EngNumber('-220m')) == '440m' 170 | assert str(-2.0 * EngNumber('-220m')) == '440m' 171 | 172 | 173 | def test_enum_div(): 174 | # positive_numbers 175 | assert str(EngNumber('220m') / EngNumber('2')) == '110m' 176 | assert str(EngNumber('220m') / 2) == '110m' 177 | assert str(EngNumber('220m') / 2.0) == '110m' 178 | 179 | assert str(2 / EngNumber('220m')) == '9.09' 180 | assert str(2.0 / EngNumber('220m')) == '9.09' 181 | 182 | # negative_numbers 183 | assert str(EngNumber('-220m') / EngNumber('-2')) == '110m' 184 | assert str(EngNumber('-220m') / -2) == '110m' 185 | assert str(EngNumber('-220m') / -2.0) == '110m' 186 | 187 | assert str(-2 / EngNumber('-220m')) == '9.09' 188 | assert str(-2.0 / EngNumber('-220m')) == '9.09' 189 | 190 | 191 | def test_enum_eq(): 192 | # positive_numbers 193 | assert EngNumber('220k') == EngNumber(220000) 194 | assert EngNumber('220k') == 220000 195 | assert EngNumber('220k') == 220000.0 196 | 197 | assert 220000 == EngNumber('220k') 198 | assert 220000.0 == EngNumber('220k') 199 | 200 | # positive_numbers 201 | assert EngNumber('-220k') == EngNumber(-220000) 202 | assert EngNumber('-220k') == -220000 203 | assert EngNumber('-220k') == -220000.0 204 | 205 | assert -220000 == EngNumber('-220k') 206 | assert -220000.0 == EngNumber('-220k') 207 | 208 | assert not (EngNumber('220k') == object) 209 | 210 | 211 | def test_enum_gt(): 212 | # positive_numbers 213 | assert EngNumber('220k') > 219000 214 | 215 | # negative_numbers 216 | assert EngNumber('-220k') < -219000 217 | 218 | 219 | def test_enum_lt(): 220 | # positive_numbers 221 | assert EngNumber('220k') < 221000 222 | 223 | # negative_numbers 224 | assert EngNumber('-220k') > -221000 225 | 226 | 227 | def test_enum_ge(): 228 | # positive_numbers 229 | assert EngNumber('220k') >= 219000 230 | assert EngNumber('220k') >= 220000 231 | 232 | # negative_numbers 233 | assert EngNumber('-220k') <= -219000 234 | assert EngNumber('-220k') <= -220000 235 | 236 | 237 | def test_enum_le(): 238 | # positive_numbers 239 | assert EngNumber('220k') <= 221000 240 | assert EngNumber('220k') <= 220000 241 | 242 | # negative_numbers 243 | assert EngNumber('-220k') >= -221000 244 | assert EngNumber('-220k') >= -220000 245 | 246 | 247 | def test_enum_to_int(): 248 | # positive_numbers 249 | assert int(EngNumber('220k')) == 220000 250 | assert int(EngNumber('220m')) == 0 251 | 252 | # negative_numbers 253 | assert int(EngNumber('-220k')) == -220000 254 | assert int(EngNumber('-220m')) == 0 255 | 256 | 257 | def test_enum_to_float(): 258 | # positive_numbers 259 | assert float(EngNumber('220k')) == 220000.0 260 | assert float(EngNumber('220m')) == 0.220 261 | 262 | # negative_numbers 263 | assert float(EngNumber('-220k')) == -220000.0 264 | assert float(EngNumber('-220m')) == -0.220 265 | 266 | 267 | def test_to_pn(): 268 | # positive_numbers 269 | assert EngNumber('1.2M').to_pn() == '1M20' 270 | assert EngNumber('220M').to_pn() == '220M' 271 | 272 | assert EngNumber('220k').to_pn() == '220k' 273 | assert EngNumber('1.2k').to_pn() == '1k20' 274 | 275 | assert EngNumber('220').to_pn() == '220' 276 | assert EngNumber('1.2').to_pn() == '1.20' 277 | 278 | assert EngNumber('220m').to_pn() == '220m' 279 | assert EngNumber('1.2m').to_pn() == '1m20' 280 | 281 | # negative_numbers 282 | assert EngNumber('-1.2M').to_pn() == '-1M20' 283 | assert EngNumber('-220M').to_pn() == '-220M' 284 | 285 | assert EngNumber('-220k').to_pn() == '-220k' 286 | assert EngNumber('-1.2k').to_pn() == '-1k20' 287 | 288 | assert EngNumber('-220').to_pn() == '-220' 289 | assert EngNumber('-1.2').to_pn() == '-1.20' 290 | 291 | assert EngNumber('-220m').to_pn() == '-220m' 292 | assert EngNumber('-1.2m').to_pn() == '-1m20' 293 | 294 | 295 | def test_to_pn_with_letter(): 296 | # positive_numbers 297 | assert EngNumber('1.2').to_pn('R') == '1R20' 298 | assert EngNumber(22.0).to_pn('C') == '22' 299 | assert EngNumber(22.1).to_pn('C') == '22C10' 300 | 301 | # negative_numbers 302 | assert EngNumber('-1.2').to_pn('R') == '-1R20' 303 | assert EngNumber(-22.0).to_pn('C') == '-22' 304 | assert EngNumber(-22.1).to_pn('C') == '-22C10' 305 | 306 | 307 | def test_enum_to_enum(): 308 | # positive_numbers 309 | enum = EngNumber('1.2') 310 | assert str(EngNumber(enum)) == '1.20' 311 | 312 | # negative_numbers 313 | enum = EngNumber('-1.2') 314 | assert str(EngNumber(enum)) == '-1.20' 315 | 316 | 317 | ''' tests for EngUnit()''' 318 | 319 | 320 | def test_to_str(): 321 | # positive_numbers 322 | assert str(EngUnit('220')) == '220' 323 | assert str(EngUnit('220ohm')) == '220ohm' 324 | assert str(EngUnit('220ohm', separator=" ")) == '220 ohm' 325 | 326 | # negative_numbers 327 | assert str(EngUnit('-220')) == '-220' 328 | assert str(EngUnit('-220ohm')) == '-220ohm' 329 | assert str(EngUnit('-220ohm', separator=" ")) == '-220 ohm' 330 | 331 | assert EngUnit('220ohm').unit == 'ohm' 332 | assert EngUnit('220', unit='ohm').unit == 'ohm' 333 | 334 | assert EngUnit('2m', unit='meter').unit == 'meter' 335 | assert EngUnit('2m', unit='meter').eng_num == EngNumber('2m') 336 | 337 | 338 | def test_to_str_large(): 339 | # positive_numbers 340 | assert str(EngUnit('220kHz')) == '220kHz' 341 | assert str(EngUnit('220000')) == '220k' 342 | assert str(EngUnit(220000)) == '220k' 343 | assert str(EngUnit(220000.00)) == '220k' 344 | assert str(EngUnit(220001.25)) == '220k' 345 | 346 | # negative_numbers 347 | assert str(EngUnit('-220kHz')) == '-220kHz' 348 | assert str(EngUnit('-220000')) == '-220k' 349 | assert str(EngUnit(-220000)) == '-220k' 350 | assert str(EngUnit(-220000.00)) == '-220k' 351 | assert str(EngUnit(-220001.25)) == '-220k' 352 | 353 | 354 | def test_to_str_small(): 355 | # positive_numbers 356 | assert str(EngUnit('220mohm')) == '220mohm' 357 | assert str(EngUnit('0.220')) == '220m' 358 | assert str(EngUnit(0.220)) == '220m' 359 | assert str(EngUnit(0.220000125)) == '220m' 360 | 361 | # negative_numbers 362 | assert str(EngUnit('-220mohm')) == '-220mohm' 363 | assert str(EngUnit('-0.220')) == '-220m' 364 | assert str(EngUnit(-0.220)) == '-220m' 365 | assert str(EngUnit(-0.220000125)) == '-220m' 366 | 367 | 368 | def test_add(): 369 | # positive_numbers 370 | assert str(EngUnit('220mHz') + EngUnit('10mHz')) == '230mHz' 371 | assert str(EngUnit('220mohm') + EngUnit('220uohm')) == '220.22mohm' 372 | assert str(EngUnit('220m') + EngUnit('220n')) == '220m' 373 | 374 | assert str(EngUnit('220m') + 0.01) == '230m' 375 | assert str(0.01 + EngUnit('220m')) == '230m' 376 | 377 | with pytest.raises(AttributeError): 378 | EngUnit('220mHz') + EngUnit('10m') 379 | with pytest.raises(AttributeError): 380 | EngUnit('10m') + EngUnit('220mHz') 381 | 382 | # negative_numbers 383 | assert str(EngUnit('-220mHz') + EngUnit('-10mHz')) == '-230mHz' 384 | assert str(EngUnit('-220mohm') + EngUnit('-220uohm')) == '-220.22mohm' 385 | assert str(EngUnit('-220m') + EngUnit('-220n')) == '-220m' 386 | 387 | assert str(EngUnit('-220m') + -0.01) == '-230m' 388 | assert str(-0.01 + EngUnit('-220m')) == '-230m' 389 | 390 | with pytest.raises(AttributeError): 391 | EngUnit('-220mHz') + EngUnit('-10m') 392 | with pytest.raises(AttributeError): 393 | EngUnit('-10m') + EngUnit('-220mHz') 394 | 395 | 396 | def test_sub(): 397 | # positive_numbers 398 | assert str(EngUnit('220mHz') - EngUnit('10mHz')) == '210mHz' 399 | assert str(EngUnit('220mohm') - EngUnit('220uohm')) == '219.78mohm' 400 | assert str(EngUnit('220m') - EngUnit('220n')) == '220m' 401 | 402 | assert str(EngUnit('220m') - 0.01) == '210m' 403 | assert str(0.220 - EngUnit('0.01')) == '210m' 404 | 405 | with pytest.raises(AttributeError): 406 | EngUnit('220mHz') - EngUnit('10m') 407 | with pytest.raises(AttributeError): 408 | EngUnit('10m') - EngUnit('220mHz') 409 | with pytest.raises(AttributeError): 410 | 10.0 - EngUnit('220mHz') 411 | 412 | # negative_numbers 413 | assert str(EngUnit('-220mHz') - EngUnit('-10mHz')) == '-210mHz' 414 | assert str(EngUnit('-220mohm') - EngUnit('-220uohm')) == '-219.78mohm' 415 | assert str(EngUnit('-220m') - EngUnit('-220n')) == '-220m' 416 | 417 | assert str(EngUnit('-220m') - -0.01) == '-210m' 418 | assert str(-0.220 - EngUnit('-0.01')) == '-210m' 419 | 420 | with pytest.raises(AttributeError): 421 | EngUnit('-220mHz') - EngUnit('-10m') 422 | with pytest.raises(AttributeError): 423 | EngUnit('-10m') - EngUnit('-220mHz') 424 | with pytest.raises(AttributeError): 425 | -10.0 - EngUnit('-220mHz') 426 | 427 | 428 | def test_mul(): 429 | # positive_numbers 430 | assert str(EngUnit('220ms') * EngUnit('2Hz')) == '440msHz' 431 | assert str(EngUnit('220ms') * EngUnit('2')) == '440ms' 432 | assert str(EngUnit('220m') * EngUnit('2s')) == '440ms' 433 | 434 | assert str(EngUnit('220ms') * 2) == '440ms' 435 | assert str(EngUnit('220ms') * 2.0) == '440ms' 436 | 437 | assert str(2 * EngUnit('220ms')) == '440ms' 438 | assert str(2.0 * EngUnit('220ms')) == '440ms' 439 | 440 | # negative_numbers 441 | assert str(EngUnit('-220ms') * EngUnit('-2Hz')) == '440msHz' 442 | assert str(EngUnit('-220ms') * EngUnit('-2')) == '440ms' 443 | assert str(EngUnit('-220m') * EngUnit('-2s')) == '440ms' 444 | 445 | assert str(EngUnit('-220ms') * -2) == '440ms' 446 | assert str(EngUnit('-220ms') * -2.0) == '440ms' 447 | 448 | assert str(-2 * EngUnit('-220ms')) == '440ms' 449 | assert str(-2.0 * EngUnit('-220ms')) == '440ms' 450 | 451 | 452 | def test_div(): 453 | # positive_numbers 454 | assert str(EngUnit('220ms') / EngUnit('2s')) == '110ms/s' 455 | assert str(EngUnit('220ms') / EngUnit('2')) == '110ms' 456 | assert str(EngUnit('220m') / EngUnit('2s')) == '110m/s' 457 | 458 | assert str(EngUnit('220ms') / 2) == '110ms' 459 | assert str(EngUnit('220ms') / 2.0) == '110ms' 460 | 461 | assert str(2 / EngUnit('220ms')) == '9.09/s' 462 | assert str(2.0 / EngUnit('220ms')) == '9.09/s' 463 | 464 | # negative_numbers 465 | assert str(EngUnit('-220ms') / EngUnit('-2s')) == '110ms/s' 466 | assert str(EngUnit('-220ms') / EngUnit('-2')) == '110ms' 467 | assert str(EngUnit('-220m') / EngUnit('-2s')) == '110m/s' 468 | 469 | assert str(EngUnit('-220ms') / -2) == '110ms' 470 | assert str(EngUnit('-220ms') / -2.0) == '110ms' 471 | 472 | assert str(-2 / EngUnit('-220ms')) == '9.09/s' 473 | assert str(-2.0 / EngUnit('-220ms')) == '9.09/s' 474 | 475 | 476 | def test_eq(): 477 | # positive_numbers 478 | assert EngUnit('220k') == EngUnit(220000) 479 | assert EngUnit('220k') == 220000 480 | assert EngUnit('220k') == 220000.0 481 | 482 | assert 220000 == EngUnit('220k') 483 | assert 220000.0 == EngUnit('220k') 484 | 485 | with pytest.raises(AttributeError): 486 | EngUnit('220mHz') == EngUnit('0.220ohm') 487 | with pytest.raises(AttributeError): 488 | EngUnit('220mHz') == 10 489 | with pytest.raises(AttributeError): 490 | EngUnit('220mHz') == 10.0 491 | assert not (EngUnit('220k') == object) 492 | 493 | # negative_numbers 494 | assert EngUnit('-220k') == EngUnit(-220000) 495 | assert EngUnit('-220k') == -220000 496 | assert EngUnit('-220k') == -220000.0 497 | 498 | assert -220000 == EngUnit('-220k') 499 | assert -220000.0 == EngUnit('-220k') 500 | 501 | with pytest.raises(AttributeError): 502 | EngUnit('-220mHz') == EngUnit('-0.220ohm') 503 | with pytest.raises(AttributeError): 504 | EngUnit('-220mHz') == -10 505 | with pytest.raises(AttributeError): 506 | EngUnit('-220mHz') == -10.0 507 | 508 | 509 | def test_gt(): 510 | # positive_numbers 511 | assert EngUnit('220kohm') > EngUnit('219000ohm') 512 | 513 | with pytest.raises(AttributeError): 514 | EngUnit('220kohm') > 219000 515 | 516 | # negative_numbers 517 | assert EngUnit('-220kohm') < EngUnit('-219000ohm') 518 | 519 | with pytest.raises(AttributeError): 520 | EngUnit('-220kohm') < -219000 521 | 522 | 523 | def test_lt(): 524 | # positive_numbers 525 | assert EngUnit('220kohm') < EngUnit('221000ohm') 526 | 527 | with pytest.raises(AttributeError): 528 | EngUnit('220kohm') < 221000 529 | 530 | # negative_numbers 531 | # ##ERROR:assert EngUnit('-220kohm') > EngUnit('-221000ohm') 532 | 533 | # ##ERROR:with pytest.raises(AttributeError): 534 | # ##ERROR: EngUnit('-220kohm') > -221000 535 | 536 | 537 | def test_ge(): 538 | # positive_numbers 539 | assert EngUnit('220kohm') >= EngUnit('219000ohm') 540 | assert EngUnit('220kohm') >= EngUnit('220000ohm') 541 | 542 | with pytest.raises(AttributeError): 543 | EngUnit('220kohm') >= 219000 544 | 545 | # negative_numbers 546 | assert EngUnit('-220kohm') <= EngUnit('-219000ohm') 547 | assert EngUnit('-220kohm') <= EngUnit('-220000ohm') 548 | 549 | with pytest.raises(AttributeError): 550 | EngUnit('-220kohm') <= -219000 551 | 552 | 553 | def test_le(): 554 | # positive_numbers 555 | assert EngUnit('220kohm') <= EngUnit('221000ohm') 556 | assert EngUnit('220kohm') <= EngUnit('220000ohm') 557 | 558 | with pytest.raises(AttributeError): 559 | EngUnit('220kohm') >= 219000 560 | with pytest.raises(AttributeError): 561 | 219000 >= EngUnit('220kohm') 562 | 563 | # negative_numbers 564 | assert EngUnit('-220kohm') >= EngUnit('-221000ohm') 565 | assert EngUnit('-220kohm') >= EngUnit('-220000ohm') 566 | 567 | with pytest.raises(AttributeError): 568 | EngUnit('-220kohm') <= -219000 569 | with pytest.raises(AttributeError): 570 | -219000 <= EngUnit('-220kohm') 571 | 572 | 573 | def test_to_int(): 574 | # positive_numbers 575 | assert int(EngUnit('220k')) == 220000 576 | assert int(EngUnit('220m')) == 0 577 | 578 | # negative_numbers 579 | assert int(EngUnit('-220k')) == -220000 580 | assert int(EngUnit('-220m')) == -0 581 | 582 | 583 | def test_to_float(): 584 | # positive_numbers 585 | assert float(EngUnit('220k')) == 220000.0 586 | assert float(EngUnit('220m')) == 0.220 587 | 588 | # negative_numbers 589 | assert float(EngUnit('-220k')) == -220000.0 590 | assert float(EngUnit('-220m')) == -0.220 591 | --------------------------------------------------------------------------------