unitx 0.6.2__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {unitx-0.6.2 → unitx-0.7.0}/CHANGELOG.md +89 -0
  2. {unitx-0.6.2 → unitx-0.7.0}/PKG-INFO +179 -33
  3. {unitx-0.6.2 → unitx-0.7.0}/README.md +178 -32
  4. {unitx-0.6.2 → unitx-0.7.0}/pyproject.toml +1 -1
  5. {unitx-0.6.2 → unitx-0.7.0}/pyrightconfig.json +3 -0
  6. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/_numerics.py +16 -5
  7. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/_unicode.py +5 -4
  8. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/dimension.py +5 -4
  9. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/factor.py +30 -11
  10. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/monomial.py +5 -5
  11. unitx-0.7.0/src/unitx/quantity.py +687 -0
  12. unitx-0.7.0/src/unitx/si/bases.py +91 -0
  13. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/si/constants.py +65 -75
  14. unitx-0.7.0/src/unitx/si/units.py +178 -0
  15. unitx-0.7.0/src/unitx/singleunit.py +160 -0
  16. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/unit.py +29 -19
  17. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/unitparser.py +47 -16
  18. unitx-0.7.0/src/unitx/unitregistry.py +305 -0
  19. unitx-0.6.2/tests/test_unit_data.py → unitx-0.7.0/tests/test_builtin_definitions.py +21 -21
  20. {unitx-0.6.2 → unitx-0.7.0}/tests/test_dimension.py +10 -8
  21. {unitx-0.6.2 → unitx-0.7.0}/tests/test_factor.py +87 -5
  22. {unitx-0.6.2 → unitx-0.7.0}/tests/test_monomial.py +8 -11
  23. unitx-0.7.0/tests/test_quantity.py +1159 -0
  24. {unitx-0.6.2 → unitx-0.7.0}/tests/test_si.py +70 -2
  25. {unitx-0.6.2 → unitx-0.7.0}/tests/test_singleunit.py +35 -15
  26. {unitx-0.6.2 → unitx-0.7.0}/tests/test_unit.py +36 -9
  27. {unitx-0.6.2 → unitx-0.7.0}/tests/test_unit_definition.py +51 -44
  28. {unitx-0.6.2 → unitx-0.7.0}/tests/test_unitparser.py +78 -0
  29. unitx-0.7.0/tests/test_unitregistry.py +204 -0
  30. {unitx-0.6.2 → unitx-0.7.0}/tests/test_warnings.py +19 -19
  31. {unitx-0.6.2 → unitx-0.7.0}/tests/typing/check_quantity.py +91 -15
  32. {unitx-0.6.2 → unitx-0.7.0}/tests/typing/check_si.py +6 -3
  33. unitx-0.6.2/src/unitx/_unit_data.py +0 -193
  34. unitx-0.6.2/src/unitx/quantity.py +0 -393
  35. unitx-0.6.2/src/unitx/si/units.py +0 -193
  36. unitx-0.6.2/src/unitx/singleunit.py +0 -208
  37. unitx-0.6.2/tests/test_quantity.py +0 -505
  38. {unitx-0.6.2 → unitx-0.7.0}/.github/workflows/ci.yml +0 -0
  39. {unitx-0.6.2 → unitx-0.7.0}/.github/workflows/release.yml +0 -0
  40. {unitx-0.6.2 → unitx-0.7.0}/.gitignore +0 -0
  41. {unitx-0.6.2 → unitx-0.7.0}/LICENSE +0 -0
  42. {unitx-0.6.2 → unitx-0.7.0}/conftest.py +0 -0
  43. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/__init__.py +0 -0
  44. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/_constclass.py +0 -0
  45. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/exceptions.py +0 -0
  46. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/py.typed +0 -0
  47. {unitx-0.6.2 → unitx-0.7.0}/src/unitx/si/__init__.py +0 -0
  48. {unitx-0.6.2 → unitx-0.7.0}/tests/__init__.py +0 -0
  49. {unitx-0.6.2 → unitx-0.7.0}/tests/test_import.py +0 -0
  50. {unitx-0.6.2 → unitx-0.7.0}/tests/test_numeric.py +0 -0
  51. {unitx-0.6.2 → unitx-0.7.0}/tests/test_typing.py +0 -0
  52. {unitx-0.6.2 → unitx-0.7.0}/tests/test_unicode.py +0 -0
@@ -1,5 +1,94 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.0] - 2026-10-19
4
+
5
+ ### Added
6
+
7
+ - Add `[registered name]` references as single factors in unit expressions,
8
+ including products, reciprocals, parentheses, and rational powers. References
9
+ resolve names and name aliases only, with whitespace normalization and no
10
+ expression evaluation or nested quoting.
11
+ - Support standalone hyphenated names and adjacent Unicode integer superscripts
12
+ such as `m²`, `s⁻¹²`, and `[metre]²`.
13
+ - Extend `prefixable: PrefixPolicy = True` on all `.define()` entry points to
14
+ accept a tuple of exact registered symbols, names, or aliases. Only those
15
+ spellings accept prefixes; True enables all spellings and False or () disables
16
+ all spellings. `PrefixPolicy` is `bool | tuple[str, ...]`.
17
+ - Enable prefixes on `arcsec` and `arcsecond` while keeping angle punctuation
18
+ unprefixable. Register unprefixable are (`a`) and hectare (`ha`), and reactive
19
+ power (`var`, name `volt-ampere reactive`). Keep `as` as attosecond and `yr`
20
+ as a 365-day year.
21
+ - Add the Julian year `a_j` (exactly 31,557,600 seconds) and its twelfth,
22
+ `mo`, as a fixed month interval distinct from variable calendar months.
23
+ - Add light year (`ly`), unified atomic mass unit (`u`), and common prefixed
24
+ namespace attributes, including attosecond (`as_`) and petaelectronvolt (`PeV`).
25
+
26
+ ### Changed
27
+
28
+ - Move definitions, spelling resolution, and registration validation into
29
+ `unitx.unitregistry`, shared by SingleUnit and UnitParser. Preserve formal identities,
30
+ exact SI factors, existing `.define()` defaults, and validation before mutation.
31
+ - Remove the superseded private `_unit_data` module. Name definition records
32
+ `PrefixDefinition` and `UnitDefinition`, with explicit definition indexes and
33
+ alias-to-symbol mappings.
34
+ - Select permitted canonical output spellings after applying binding policies.
35
+ When only a name permits a prefix, expressions use `[name]` so repr and pickle
36
+ preserve the prefixed identity. Expose `PrefixPolicy` for type annotations.
37
+ - Use the finite spelling exceptions `megohm` and `kilohm` in preferred names,
38
+ retaining `megaohm` and `kiloohm` as accepted inputs.
39
+ - Reuse Unicode superscript tables, handle malformed name brackets through
40
+ the common lexical error path, and simplify definition initialization,
41
+ preferred spelling selection, and generated prefix-name validation.
42
+ - Share coherent SI units and the seven defining constants through private
43
+ bases. Derive `ly` from `c` and the Julian year, and `eV` from `e` and `V`,
44
+ preserving the public `si` and `SI` namespaces and constant magnitude types.
45
+
46
+ ### Fixed
47
+
48
+ - Register metre of mercury (`mHg`) as the prefixable unit and construct `mmHg`
49
+ from its milli prefix. Preserve `760 mmHg = 1 atm` and the existing Torr scale,
50
+ keep the definition exact, and reject stacked prefixes such as `kmmHg`.
51
+
52
+ ### Migration
53
+
54
+ - Square brackets and `⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻` are now reserved grammar characters and can
55
+ no longer occur in registered symbols or names. Rename custom spellings that
56
+ previously used them before rebuilding or unpickling those definitions.
57
+ - Long names remain whole-input lookups unless explicitly referenced in brackets.
58
+ `str()` remains readable display and is not a general serialization format;
59
+ use `expression()` for formal unit products and repr for ScaledUnit state.
60
+ - `mmHg` now has the formal identity of milli-prefixed `mHg`; rebuild stored
61
+ units when moving between versions. Its pressure conversion scale is unchanged.
62
+ `kmmHg` and names with repeated prefixes are no longer accepted.
63
+ - Registering `u` makes ASCII micro spellings such as `um` issue the existing
64
+ ambiguity warning. Their meaning is unchanged; use `µm`, `μm`, or `micrometre`
65
+ to avoid the warning. `kt` remains a knot alias and tonne stays unprefixable.
66
+
67
+ ## [0.6.3] - 2026-10-18
68
+
69
+ ### Added
70
+
71
+ - Add `Quantity.value_in(unit)` to return the same magnitude and concrete type as `.to(unit).value` without constructing a temporary quantity. The target unit is required and accepts a string or Unit, including ScaledUnit.
72
+ - Add `Quantity.isclose()` for symmetric real scalar comparisons with relative and absolute tolerances in coherent SI units. Nonzero absolute tolerances for dimensional quantities must carry matching units. Rational coefficients remain exact; residual irrational factors are approximated. Zero tolerances use exact equality.
73
+ - Make quantity truth values follow their magnitudes, so zero quantities are false regardless of unit.
74
+
75
+ ### Changed
76
+
77
+ - Apply native magnitude operators in operand order for arithmetic, unary operations, and numerical powers, preserving numerical promotion, Decimal contexts, signals, and exceptions. Support the scalar operand types accepted by the magnitude, including complex values and custom scalars.
78
+ - Keep Decimal conversions and arithmetic in Decimal. Rational conversions combine the magnitude and scale exactly before rounding once in the active context; identity conversions preserve the original magnitude. Addition and subtraction align integer operands in the Decimal domain when paired with Decimal magnitudes. Derived SI constants use Decimal arithmetic and `Decimal(str(math.pi))` where an approximate pi is required, with evaluation in the context active at import.
79
+ - Interpret float formal exponents by their shortest decimal text, without a denominator limit, consistently across Monomial, Dimension, Factor, SymbolicConstant, Unit, and Quantity. For example, `0.1` represents `1/10` and `1e-10` remains nonzero. Numeric scale inputs to Factor retain their existing denominator-limit and warning rules.
80
+ - Cache rationalized Factor decompositions lazily, independently of numerical value evaluation, while preserving equality, hashing, copying, and serialization.
81
+ - Update public type hints, examples, and regression coverage for conversion, native arithmetic, comparisons, and formal exponents.
82
+
83
+ ### Fixed
84
+
85
+ - Preserve native comparisons, including independent `!=`, Decimal signals, and complex ordering errors, when unit factors agree. Across different factors, compare builtin real values and complex components using exact coefficients, retaining consistent hashes for equal quantities and native unhashability for custom magnitudes.
86
+
87
+ ### Migration
88
+
89
+ - Decimal arithmetic no longer converts operands to Fraction automatically. Mixing Decimal with float or Fraction in arithmetic now raises the native TypeError; explicitly choose a common numerical type when needed. Decimal conversion and derived SI constants can therefore have different result types and rounding from earlier releases.
90
+ - Quantity numerical powers use the original exponent object; for example, an integer magnitude raised to `2.0` produces a float. Use `Fraction(1, 3)` for an exactly one-third formal exponent: the float `1 / 3` now represents the decimal `0.3333333333333333` and does not simplify as exactly one third.
91
+
3
92
  ## [0.6.2] - 2026-10-18
4
93
 
5
94
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: unitx
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: A modern, intuitive Python library for units, quantities, and dimensional analysis.
5
5
  Project-URL: Homepage, https://github.com/DaitTHU/unitx
6
6
  Project-URL: Issues, https://github.com/DaitTHU/unitx/issues
@@ -107,8 +107,9 @@ text spellings without adding an attribute to `si`.
107
107
 
108
108
  SI defining constants retain their exact decimal values. Measured constants
109
109
  are stored nominal values; measurement uncertainties are not propagated.
110
- Derived constants involving numerical approximations, such as `math.pi`, are
111
- evaluated numerically.
110
+ Derived Decimal constants are evaluated in the Decimal context active at
111
+ import. Formulas involving pi use `Decimal(str(math.pi))` as an approximation;
112
+ additional Decimal precision does not add precision to pi.
112
113
 
113
114
  ## Constructing units and quantities
114
115
 
@@ -192,16 +193,23 @@ grouped denominator factors and multiplication dots between adjacent numbers.
192
193
 
193
194
  ## Conversion and arithmetic
194
195
 
195
- Use `.to()` to convert between units with the same physical dimensions.
196
+ Use `.to()` to obtain a new quantity in a unit with the same physical dimensions,
197
+ or `.value_in(unit)` to obtain only its numerical value. The target unit is
198
+ required and accepts a string or `Unit`, including `ScaledUnit`.
199
+ `.value_in(unit)` returns the same value and concrete type as `.to(unit).value`,
200
+ without constructing a temporary quantity. Both follow the conversion rules
201
+ below. A conversion with scale one preserves the original magnitude; Decimal
202
+ magnitudes stay Decimal and other conversions can change the numerical type.
203
+ `.value` reads the original stored magnitude.
196
204
  Addition and subtraction convert the right operand to the left operand's unit:
197
205
 
198
206
  ```pycon
199
207
  >>> length = Quantity(1, 'm')
200
208
  >>> print(length + Quantity(50, 'cm'))
201
209
  3/2 m
202
- >>> length.to('cm').value
210
+ >>> length.value_in('cm')
203
211
  100
204
- >>> Quantity(1, 'cm').to('m').value
212
+ >>> Quantity(1, 'cm').value_in('m')
205
213
  Fraction(1, 100)
206
214
 
207
215
  ```
@@ -238,14 +246,25 @@ False
238
246
  token: `ms` is a millisecond, while `m s` is metre times second.
239
247
  - Powers can use adjacent numbers, `^`, or `**`. Exponents written as ratios
240
248
  require parentheses, as in `m^(1/2)`; decimal exponents such as `m^0.5` are exact.
249
+ Adjacent integer superscripts are also accepted: `m²`, `s⁻¹²`, and `(m/s)²`.
250
+ Superscript fractions and mixed exponent notations are not supported.
241
251
  - Multiplication and division associate left to right. Use `m/(s A)` to put
242
252
  both `s` and `A` in the denominator; `m/s A` means `(m/s)*A`.
243
253
  - Unit symbols and names are case-sensitive. `u`, `µ`, and `μ` are accepted
244
254
  micro prefixes. Use lowercase `k` for kilo; `K` denotes kelvin.
245
- - A registered multiword name is recognized before implicit multiplication
255
+ - A registered multiword or hyphenated name is recognized before implicit multiplication
246
256
  when it occupies the entire input. Whitespace between its words is treated
247
- as a single space. Inside products, parentheses, reciprocals, and powers,
248
- use its symbol or combine `Unit` objects instead.
257
+ as a single space. Inside expressions, use `[name]` to make a name one factor,
258
+ for example `[astronomical unit] / s` or `[volt-ampere reactive]**2`.
259
+ Symbols and `Unit` object composition remain available.
260
+
261
+ Square brackets reference registered names and name aliases only: `[metre]`
262
+ works, while `[m]` and `[m/s]` do not. The contents are never evaluated as an
263
+ expression. Empty, nested, and unmatched brackets are errors. Put permitted
264
+ name prefixes inside the brackets, as in `[kiloastronomical unit]`; `k[name]`
265
+ does not apply a prefix. Single and double quotes retain their angle-unit
266
+ meaning rather than acting as string delimiters. Bare compound names are
267
+ not guessed from adjacent words, and ordinary parentheses still group products.
249
268
 
250
269
  The unit table contains `s`, `m`, `g`, `A`, `K`, `mol`, and `cd`, plus the
251
270
  dimensionless identity. Importing unitx also initializes the common units in
@@ -295,9 +314,10 @@ aliases gives an empty tuple. `symbol_aliases` accepts a string or tuple of
295
314
  strings; it and `prefixable` must be passed by keyword.
296
315
  Symbols and symbol aliases must each be a single printable unit token.
297
316
  Names and name aliases may contain multiple such tokens separated by single
298
- ASCII spaces, without leading or trailing spaces. Decimal digits and parser
299
- operators are not allowed in any token. The primary symbol and `name` are
300
- required and must be nonempty; empty aliases are ignored.
317
+ ASCII spaces or hyphens, without leading or trailing separators. Decimal digits
318
+ and parser operators are not allowed in any token. The primary symbol and `name` are
319
+ required and must be nonempty; empty aliases are ignored. Square brackets and
320
+ the superscript characters `⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻` are reserved for parser syntax.
301
321
 
302
322
  ```pycon
303
323
  >>> span = (2 * si.m).define('span', 'survey span', 'survey distance')
@@ -307,22 +327,86 @@ True
307
327
  True
308
328
  >>> Unit('survey span') / si.s == Unit('span / s')
309
329
  True
330
+ >>> Unit('[survey span] / [second]') == Unit('span / s')
331
+ True
310
332
 
311
333
  ```
312
334
 
313
- Multiword names are recognized only as complete inputs. For example,
335
+ Bare multiword names are recognized only as complete inputs. For example,
314
336
  `Unit('astronomical unit / arcsec')` does not recognize `astronomical unit`
315
- as a factor; use one of the two compound forms above instead.
337
+ as a factor; use `[astronomical unit] / arcsec`, symbols, or Unit objects instead.
338
+
339
+ SI prefixes are enabled by default. `prefixable: PrefixPolicy = True` accepts
340
+ `True` for all spellings, `False` for none, or a tuple of exact symbols, names,
341
+ and aliases supplied in that definition. With a tuple, only the listed
342
+ spellings accept prefixes; an empty tuple disables prefixes. Unknown spellings
343
+ and non-string entries are errors. For example,
344
+ `prefixable=('arcsec', 'arcsecond')` enables all SI prefixes on those two
345
+ spellings, while other spellings of that unit remain unprefixable.
346
+
347
+ The tuple selects **unit spellings**, not prefix symbols. Prefix names attach
348
+ directly to the first word of a multiword name, as in `kiloastronomical unit`;
349
+ a separate `kilo astronomical unit` is not accepted. `PrefixPolicy` is available
350
+ from `unitx.unitregistry` for annotations and means `bool | tuple[str, ...]`.
351
+
352
+ ```pycon
353
+ >>> tick = si.s.define(
354
+ ... 'demoTick',
355
+ ... 'survey interval',
356
+ ... symbol_aliases='demoT',
357
+ ... prefixable=('demoT', 'survey interval'),
358
+ ... )
359
+ >>> Unit('[millisurvey interval]') == Unit('mdemoT')
360
+ True
361
+ >>> Quantity(1000, 'mdemoT').to(tick).value
362
+ Fraction(1, 1)
363
+ >>> Unit('mdemoT').expression()
364
+ 'mdemoT'
365
+
366
+ ```
367
+
368
+ Prefix permissions are checked before alias normalization. Canonical identity
369
+ still uses the original registered unit and canonical prefix, independent of
370
+ the input alias. Preferred spellings use the first permitted symbol or alias;
371
+ if only a name supports a prefix, `SingleUnit.symbol` falls back to that name
372
+ and `Unit.expression()` emits a `[name]` reference. Conversely, `SingleUnit.name`
373
+ falls back to a symbol if no name allows that prefix. Repr and pickle preserve
374
+ the same formal unit, rather than replacing the prefix with an extra scale.
375
+
376
+ `arcsec` and `arcsecond` accept prefixes, for example `marcsec` and
377
+ `milliarcsecond`; `″` and `"` do not. `as` continues to mean attosecond,
378
+ and `mas` is not an arcsecond alias. `a` (are) and `ha` (hectare) are separate
379
+ unprefixable units; `Pa` remains pascal. `yr` remains exactly 365 days.
380
+ `a_j` is the Julian year of exactly 365.25 days (31,557,600 seconds), while
381
+ `mo` is one twelfth of that interval, rather than a variable calendar month.
382
+ The name exceptions `megohm` and `kilohm` are supported alongside the older
383
+ `megaohm` and `kiloohm` spellings; `gigaohm` retains both vowels. No general
384
+ vowel-elision rule is applied.
385
+
386
+ `ly` is defined using the speed of light and the Julian year; `eV` uses the
387
+ elementary charge and volt. Both reuse the defining constants exposed by `SI`.
388
+ `mHg` is the registered metre-of-mercury unit and `mmHg` is its milli-prefixed
389
+ form; `760 * si.mmHg` converts to `si.atm` with value 1 under the library's
390
+ existing pressure convention. Prefixes do not stack: `mmmHg` and `kmmHg` are
391
+ invalid. `kt` retains its meaning as a knot alias; tonne remains unprefixable.
392
+
393
+ The newly registered `u` denotes the unified atomic mass unit. ASCII micro
394
+ spellings such as `um` still mean micrometres, but now issue `UnitSymbolWarning`
395
+ because `u m` has a separate meaning. Use `µm`, `μm`, or `micrometre` to avoid
396
+ this warning.
316
397
 
317
- SI prefixes are enabled by default; set `prefixable=False` to disable them.
318
- Prefix names attach directly to the first word of a multiword name, as in
319
- `kiloastronomical unit`; a separate `kilo astronomical unit` is not accepted.
320
398
  Registration raises `UnitRegistryError` if a new spelling, including a generated
321
399
  prefixed form, already resolves to a unit or conflicts with another spelling
322
400
  in the same definition. A multiword spelling must not replace an existing
323
401
  implicit product such as `metre second`. Validation finishes before the
324
402
  registry changes.
325
403
 
404
+ The registry and spelling rules live in `unitx.unitregistry`; parsing expressions
405
+ does not change unit definitions. A parser instance caches only a successful
406
+ result; failed parses may be retried after a definition is added. Registration
407
+ preserves existing valid spellings and validates before updating its indexes.
408
+ Concurrent registry writes and isolated registry contexts are not supported.
409
+
326
410
  Quantity definitions require positive finite `int`, `Fraction`, `Decimal`, or
327
411
  `float` magnitudes. Prefer `Fraction` or `Decimal` for exact scales. Floats use
328
412
  Factor's denominator limit and can warn or round to zero and fail, for example
@@ -368,29 +452,90 @@ Fraction(1001, 1)
368
452
 
369
453
  Exact unit algebra and numerical magnitude evaluation have different rules:
370
454
 
371
- - Rational unit scales and exponents remain exact. Prefer `Fraction` or
372
- `Decimal` for exact exponents; float exponents may be approximated with an
373
- `InexactFloatWarning`.
374
- - Conversion and arithmetic can change the magnitude's type. Finite `Decimal`
375
- operands are converted exactly to `Fraction`, without Decimal context
376
- rounding.
377
- - Other magnitudes use native numerical arithmetic. In particular, integer
378
- division produces a float, and nonintegral magnitude powers can round or
379
- produce complex values.
455
+ - Formal exponents use one rule in Monomial, Dimension, Factor, Unit, and
456
+ Quantity: int/Fraction/Decimal values stay exact; floats use their shortest
457
+ decimal text without a denominator limit. Thus `0.1` means `1/10` and
458
+ `1e-10` remains nonzero. Use `Fraction(1, 3)` for exactly one third:
459
+ the float `1 / 3` means the decimal `0.3333333333333333` in formal exponents.
460
+ Factor's numeric scale inputs retain their separate denominator-limit rules.
461
+ - Quantity handles units; the magnitudes determine numerical arithmetic.
462
+ Operators use the magnitudes in operand order and preserve native promotion,
463
+ rounding, signals, and exceptions. Decimal arithmetic returns Decimal and
464
+ follows its active context. Mixing Decimal with float or Fraction raises
465
+ `TypeError`, as it does for the bare numbers; choose a common numerical type
466
+ explicitly when needed.
467
+ - Conversion with a rational scale keeps int/Fraction values exact. Decimal
468
+ conversion combines the value and scale exactly internally, then rounds once
469
+ to Decimal in the active context. Irrational unit scales are approximated as
470
+ floats, using their shortest decimal representations for Decimal conversion.
471
+ A scale of one preserves the magnitude without arithmetic or rounding.
472
+ Direct `Factor(value)` retains its separate exact Fraction evaluation rules.
473
+ - Addition and subtraction align the right operand with the left quantity's
474
+ unit before numerical arithmetic. Integers being aligned with a Decimal
475
+ magnitude are scaled in the Decimal domain, so `Decimal('1') * si.m + 50 *
476
+ si.cm` returns a Decimal magnitude of `1.5`. Explicit Fraction/float operands
477
+ keep their own numerical domains. Finite-precision conversion and arithmetic
478
+ can round; changing the result unit can therefore change rounding errors.
479
+ - Unary `+` and `-` apply the magnitude's native operators and preserve the unit.
480
+ Decimal results remain Decimal and follow its context precision, rounding,
481
+ signals, and exceptions; unary `+` can therefore change the numerical value.
482
+ - Integer division produces a float; Fraction arithmetic remains rational.
483
+ Use Fraction magnitudes explicitly for exact rational results. Powers pass
484
+ the original exponent to the magnitude: `2 ** 2.0` returns float and
485
+ `2 ** Decimal('0.5')` returns Decimal. Decimal magnitudes require int or
486
+ Decimal exponents, as in Python. The unit follows the common formal exponent
487
+ rule above; numerical evaluation still uses the original exponent object.
488
+ Exponents must be finite real scalars other than bool. Numerical powers can
489
+ round or produce complex values, following the magnitude's rules.
490
+ - Truth values follow the magnitude, so zero quantities are false regardless
491
+ of unit. Comparisons with equal unit factors use native magnitude operators,
492
+ including independent `!=`, Decimal signals/exceptions and complex ordering
493
+ errors. For different unit factors, builtin real values and complex
494
+ components are aligned exactly to avoid rounding-dependent equality or
495
+ hashes. Ordinary numeric subclasses use the same alignment as their base
496
+ types. Decimal/float signals apply to native comparisons with equal factors;
497
+ cross-factor alignment compares exact rational coefficients and does not
498
+ perform an additional comparison merely to reproduce signals. Decimal/Fraction
499
+ comparisons are supported natively.
380
500
  - Equality is exact, without a tolerance. Finite floats are compared as their
381
501
  stored binary rational values. Floating-point conversions and irrational
382
- powers may round; compare their values approximately in a common unit when
383
- appropriate.
502
+ powers may round; use `isclose()` for approximate real scalar comparisons.
503
+ It uses the symmetric condition
504
+ `abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)` in coherent SI
505
+ units, with defaults `rel_tol=1e-9` and `abs_tol=0`. Swapping quantities
506
+ preserves the result. Nonzero absolute tolerances for dimensional quantities
507
+ must carry matching units; plain operands and plain absolute tolerances
508
+ represent unscaled dimensionless values. Tolerances must be finite and
509
+ nonnegative. Different dimensions raise `DimensionError`. NaNs are never
510
+ close, and infinities are close only to infinities of the same sign.
511
+ Finite rational coefficients stay exact; residual irrational unit factors
512
+ are approximated. With both tolerances zero, `isclose()` uses exact equality.
384
513
 
385
514
  ```pycon
386
- >>> from decimal import Decimal
515
+ >>> from decimal import Decimal, localcontext
387
516
  >>> from fractions import Fraction
517
+ >>> with localcontext() as context:
518
+ ... context.prec = 2
519
+ ... q = Quantity(Decimal('1.2345'), 'm')
520
+ ... print((+q).value, (-q).value, (q * 2).value)
521
+ 1.2 -1.2 2.5
388
522
  >>> Quantity(Decimal('0.1'), 'm').to('cm').value
389
- 10
523
+ Decimal('10')
390
524
  >>> (Quantity(Fraction(1), 'm') / Quantity(3, 's')).value
391
525
  Fraction(1, 3)
526
+ >>> (Quantity(2, 'm') ** 2.0).value
527
+ 4.0
528
+ >>> bool(Quantity(0, 'm'))
529
+ False
392
530
  >>> Quantity(0.1, '') == Quantity(Fraction(1, 10), '')
393
531
  False
532
+ >>> Quantity(0.1 + 0.2, 'm').isclose(Quantity(0.3, 'm'))
533
+ True
534
+ >>> a, b = Quantity(1, 'm'), Quantity(100.05, 'cm')
535
+ >>> a.isclose(b, rel_tol=0, abs_tol=Quantity(1, 'mm'))
536
+ True
537
+ >>> b.isclose(a, rel_tol=0, abs_tol=Quantity(1, 'mm'))
538
+ True
394
539
 
395
540
  ```
396
541
 
@@ -410,9 +555,10 @@ python -m pip install sympy
410
555
  - Custom magnitudes must support scalar arithmetic, return a Python `bool`
411
556
  from equality, and defer raw operations with `Unit` operands to its reflected
412
557
  operators.
413
- - Scalar multiplication and division of quantities currently accept `int`,
414
- `float`, `Fraction`, and `Decimal`. Complex magnitudes are supported, including
415
- results of fractional powers; ordering follows the magnitude's native rules.
558
+ - Scalar operations use the magnitude's supported operand types, including
559
+ complex numbers and custom scalar magnitudes. Custom magnitudes must also
560
+ defer operations with Quantity operands so that Quantity can handle units.
561
+ Ordering follows the magnitude's native rules.
416
562
  - Unit conversion is multiplicative. Offset temperature scales such as Celsius
417
563
  and Fahrenheit are not supported.
418
564