diff --git a/CHANGES.rst b/CHANGES.rst
index 774387872..5cb2870b8 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -38,6 +38,8 @@ New Builtins
#. ``Kurtosis``
#. ``ListLogPlot``
#. ``LogPlot``
+#. ``$MaxMachineNumber``
+#. ``$MinMachineNumber``
#. ``NumberLinePlot``
#. ``PauliMatrix``
#. ``Remove``
@@ -92,9 +94,10 @@ Bugs
#. Units and Quantities were sometimes failing. Also they were omitted from documentation.
#. Better handling of ``Infinite`` quantities.
-#. Fix ``Precision`` compatibility with WMA.
+#. Improved ``Precision`` and ``Accuracy``compatibility with WMA. In particular, ``Precision[0.]`` and ``Accuracy[0.]``
+#. Accuracy in numbers using the notation ``` n.nnn``acc ``` now is properly handled.
+
-
PyPI Package requirements
+++++++++++++++++++++++++
@@ -115,6 +118,8 @@ Enhancements
#. ``Grid`` compatibility with WMA was improved. Now it supports non-uniform list of lists and lists with general elements.
#. Support for BigEndian Big TIFF
+
+
5.0.2
-----
diff --git a/SYMBOLS_MANIFEST.txt b/SYMBOLS_MANIFEST.txt
index eb74417e7..859bb0561 100644
--- a/SYMBOLS_MANIFEST.txt
+++ b/SYMBOLS_MANIFEST.txt
@@ -40,7 +40,9 @@ System`$Machine
System`$MachineEpsilon
System`$MachineName
System`$MachinePrecision
+System`$MaxMachineNumber
System`$MaxPrecision
+System`$MinMachineNumber
System`$MinPrecision
System`$ModuleNumber
System`$OperatingSystem
@@ -245,8 +247,8 @@ System`Continue
System`ContinuedFraction
System`Convert`B64Dump`B64Decode
System`Convert`B64Dump`B64Encode
-System`ConvertersDump`$extensionMappings
-System`ConvertersDump`$formatMappings
+System`ConvertersDump`$ExtensionMappings
+System`ConvertersDump`$FormatMappings
System`CoprimeQ
System`CopyDirectory
System`CopyFile
@@ -613,7 +615,6 @@ System`MachinePrecision
System`Magenta
System`MakeBoxes
System`ManhattanDistance
-System`Manipulate
System`MantissaExponent
System`Map
System`MapAt
@@ -687,10 +688,10 @@ System`Null
System`NullSpace
System`Number
System`NumberForm
+System`NumberLinePlot
System`NumberQ
System`NumberString
System`Numerator
-System`NumberLinePlot
System`NumericFunction
System`NumericQ
System`O
@@ -739,7 +740,6 @@ System`Pi
System`Pick
System`PieChart
System`Piecewise
-System`PillowImageFilter
System`Pink
System`PixelValue
System`PixelValuePositions
@@ -781,7 +781,6 @@ System`Print
System`PrintTrace
System`Private`$ContextPathStack
System`Private`$ContextStack
-System`Private`ManipulateParameter
System`Product
System`ProductLog
System`Projection
@@ -830,6 +829,7 @@ System`Reap
System`Record
System`Rectangle
System`RectangleBox
+System`Rectangular
System`Red
System`RegularExpression
System`RegularPolygon
diff --git a/mathics/builtin/atomic/numbers.py b/mathics/builtin/atomic/numbers.py
index d874957f3..c87d21f6b 100644
--- a/mathics/builtin/atomic/numbers.py
+++ b/mathics/builtin/atomic/numbers.py
@@ -24,32 +24,35 @@
from mathics.builtin.base import Builtin, Predefined, Test
from mathics.core.atoms import (
- Complex,
Integer,
Integer0,
Integer10,
MachineReal,
- MachineReal0,
Number,
Rational,
- Real,
)
from mathics.core.attributes import A_LISTABLE, A_PROTECTED
from mathics.core.convert.python import from_python
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
-from mathics.core.number import dps, machine_epsilon, machine_precision
+from mathics.core.number import (
+ MACHINE_PRECISION_VALUE,
+ machine_epsilon,
+ machine_precision,
+)
from mathics.core.symbols import Symbol, SymbolDivide
from mathics.core.systemsymbols import (
SymbolIndeterminate,
SymbolInfinity,
SymbolLog,
+ SymbolMachinePrecision,
SymbolN,
SymbolPrecision,
SymbolRealDigits,
SymbolRound,
)
from mathics.eval.nevaluator import eval_N
+from mathics.eval.numbers import eval_Accuracy, eval_Precision
SymbolIntegerDigits = Symbol("IntegerDigits")
SymbolIntegerExponent = Symbol("IntegerExponent")
@@ -157,7 +160,8 @@ class Accuracy(Builtin):
examines the number of significant digits of $expr$ after the \
decimal point in the number x.
- This is rather a proof-of-concept than a full implementation.
+ Notice that the result could be slighly different than the obtained\
+ in WMA, due to differencs in the internal representation of the real numbers.
Accuracy of a real number is estimated from its value and its precision:
@@ -183,11 +187,17 @@ class Accuracy(Builtin):
= Infinity
>> Accuracy[F[1.3, Pi, A]]
- = 14.8861
+ = ...
'Accuracy' for the value 0 is a fixed-precision Real number:
>> 0``2
= 0.00
+ >> Accuracy[0.``2]
+ = 2.
+
+ For 0.`, the accuracy satisfies:
+ >> Accuracy[0.`] == $MachinePrecision - Log[10, $MinMachineNumber]
+ = True
In compound expressions, the 'Accuracy' is fixed by the number with
the lowest 'Accuracy':
@@ -203,33 +213,10 @@ class Accuracy(Builtin):
def eval(self, z, evaluation):
"Accuracy[z_]"
- if isinstance(z, Real):
- if z.is_zero:
- return MachineReal(dps(z.get_precision()))
- z_f = z.to_python()
- log10_z = mpmath.log((-z_f if z_f < 0 else z_f), 10)
- return MachineReal(dps(z.get_precision()) - log10_z)
-
- if isinstance(z, Complex):
- acc_real = self.eval(z.real, evaluation)
- acc_imag = self.eval(z.imag, evaluation)
- if acc_real is SymbolInfinity:
- return acc_imag
- if acc_imag is SymbolInfinity:
- return acc_real
- return Real(min(acc_real.to_python(), acc_imag.to_python()))
-
- if isinstance(z, Expression):
- result = None
- for element in z.elements:
- candidate = self.eval(element, evaluation)
- if isinstance(candidate, Real):
- candidate_f = candidate.to_python()
- if result is None or candidate_f < result:
- result = candidate_f
- if result is not None:
- return Real(result)
- return SymbolInfinity
+ acc = eval_Accuracy(z)
+ if acc is None:
+ return SymbolInfinity
+ return MachineReal(acc)
class ExactNumberQ(Test):
@@ -927,8 +914,8 @@ class Precision(Builtin):
'Precision[$expr$]'
examines the number of significant digits of $expr$.
-
- This is rather a proof-of-concept than a full implementation.
+ Notice that the result could be slighly different than the obtained\
+ in WMA, due to differencs in the internal representation of the real numbers.
The precision of an exact number, e.g. an Integer, is 'Infinity':
@@ -954,43 +941,39 @@ class Precision(Builtin):
>> Precision[{{1, 1.`},{1.`5, 1.`10}}]
= 5.
+ For non-zero Real values, it holds in general:
+
+ 'Accuracy'[$z$] == 'Precision'[$z$] + 'Log'[$z$]
+
+ >> (Accuracy[z] == Precision[z] + Log[z])/.z-> 37.`
+ = True
+
+ The case of `0.` values is special. Following WMA, in a Machine Real\
+ representation, the precision is set to 'MachinePrecision':
+ >> Precision[0.]
+ = MachinePrecision
+
+ On the other hand, for a Precision Real with fixed accuracy,\
+ the precision is evaluated to 0.:
+ >> Precision[0.``3]
+ = 0.
+
See also
:'Accuracy':
/doc/reference-of-built-in-symbols/atomic-elements-of-expressions/representation-of-numbers/accuracy/.
"""
- rules = {
- "Precision[z_?MachineNumberQ]": "MachinePrecision",
- }
-
summary_text = "find the precision of a number"
def eval(self, z, evaluation):
"Precision[z_]"
- if isinstance(z, Real):
- if z.is_zero:
- return MachineReal0
- return MachineReal(dps(z.get_precision()))
-
- if isinstance(z, Complex):
- prec_real = self.eval(z.real, evaluation)
- prec_imag = self.eval(z.imag, evaluation)
- if prec_real is SymbolInfinity:
- return prec_imag
- if prec_imag is SymbolInfinity:
- return prec_real
-
- return Real(min(prec_real.to_python(), prec_imag.to_python()))
-
- if isinstance(z, Expression):
- result = None
- for element in z.elements:
- candidate = self.eval(element, evaluation)
- if isinstance(candidate, Real):
- candidate_f = candidate.to_python()
- if result is None or candidate_f < result:
- result = candidate_f
- if result is not None:
- return Real(result)
- return SymbolInfinity
+ if isinstance(z, MachineReal):
+ return SymbolMachinePrecision
+
+ prec = eval_Precision(z)
+ if prec is None:
+ return SymbolInfinity
+ if prec == MACHINE_PRECISION_VALUE:
+ return SymbolMachinePrecision
+ return MachineReal(prec)
diff --git a/mathics/builtin/numbers/constants.py b/mathics/builtin/numbers/constants.py
index 3383802fa..3c3caeb10 100644
--- a/mathics/builtin/numbers/constants.py
+++ b/mathics/builtin/numbers/constants.py
@@ -19,7 +19,14 @@
from mathics.builtin.base import Builtin, Predefined, SympyObject
from mathics.core.atoms import MachineReal, PrecisionReal
from mathics.core.attributes import A_CONSTANT, A_PROTECTED, A_READ_PROTECTED
-from mathics.core.number import PrecisionValueError, get_precision, machine_precision
+from mathics.core.evaluation import Evaluation
+from mathics.core.number import (
+ MAX_MACHINE_NUMBER,
+ MIN_MACHINE_NUMBER,
+ PrecisionValueError,
+ get_precision,
+ machine_precision,
+)
from mathics.core.symbols import Atom, Symbol, strip_context
from mathics.core.systemsymbols import SymbolIndeterminate
@@ -575,6 +582,59 @@ class Overflow(Builtin):
summary_text = "overflow in numeric evaluation"
+class MaxMachineNumber(Predefined):
+ """
+ Largest normalizable machine number (
+ :WMA:
+ https://reference.wolfram.com/language/ref/$MaxMachineNumber.html
+ )
+
+
+ - '$MaxMachineNumber'
+
- Represents the largest positive number that can be represented \
+ as a normalized machine number in the system.
+
+
+ The product of '$MaxMachineNumber' and '$MinMachineNumber' is a constant:
+ >> $MaxMachineNumber * $MinMachineNumber
+ = 4.
+
+ """
+
+ name = "$MaxMachineNumber"
+ summary_text = "largest normalized positive machine number"
+
+ def evaluate(self, evaluation: Evaluation) -> MachineReal:
+ return MachineReal(MAX_MACHINE_NUMBER)
+
+
+class MinMachineNumber(Predefined):
+ """
+ Smallest normalizable machine number (
+ :WMA:
+ https://reference.wolfram.com/language/ref/$MinMachineNumber.html
+ )
+
+
+ - '$MinMachineNumber'
+
- Represents the smallest positive number that can be represented \
+ as a normalized machine number in the system.
+
+
+ 'MachinePrecision' minus the 'Log' base 10 of this number is the\
+ 'Accuracy' of 0`:
+ >> MachinePrecision -Log[10., $MinMachineNumber]==Accuracy[0`]
+ = True
+
+ """
+
+ name = "$MinMachineNumber"
+ summary_text = "smallest normalized positive machine number"
+
+ def evaluate(self, evaluation: Evaluation) -> MachineReal:
+ return MachineReal(MIN_MACHINE_NUMBER)
+
+
class Pi(_MPMathConstant, _SympyConstant):
"""
diff --git a/mathics/core/expression.py b/mathics/core/expression.py
index e67359131..e8b22a47e 100644
--- a/mathics/core/expression.py
+++ b/mathics/core/expression.py
@@ -54,6 +54,7 @@
SymbolDirectedInfinity,
SymbolFunction,
SymbolMinus,
+ SymbolOverflow,
SymbolPattern,
SymbolPower,
SymbolSequence,
@@ -1266,7 +1267,11 @@ def rules():
yield rule
for rule in rules():
- result = rule.apply(new, evaluation, fully=False)
+ try:
+ result = rule.apply(new, evaluation, fully=False)
+ except OverflowError:
+ evaluation.message("General", "ovfl")
+ return Expression(SymbolOverflow), False
if result is not None:
if not isinstance(result, EvalMixin):
return result, False
diff --git a/mathics/core/number.py b/mathics/core/number.py
index ba166ed61..2a5b3297e 100644
--- a/mathics/core/number.py
+++ b/mathics/core/number.py
@@ -2,28 +2,33 @@
# cython: language_level=3
import string
-from math import ceil, log, log2
+from math import ceil, log
+from sys import float_info
from typing import List, Optional
import mpmath
import sympy
+from mathics.core.element import BaseElement
from mathics.core.symbols import (
SymbolMachinePrecision,
SymbolMaxPrecision,
SymbolMinPrecision,
)
-C = log2(10) # ~ 3.3219280948873626
+C = mpmath.log(10.0, 2.0) # ~ 3.3219280948873626
-# Number of bits of machine precision.
-# Note this is a float, not an int.
-# WMA uses real values for precision, to take into account the internal representation of numbers.
-# This is why $MachinePrecision is not 16, but 15.9546`
-machine_precision = 53.0
-machine_digits = int(machine_precision / C)
+machine_precision = float_info.mant_dig
+machine_digits = float_info.dig
-machine_epsilon = 2 ** (1 - machine_precision)
+MACHINE_EPSILON = float_info.epsilon
+MACHINE_PRECISION_VALUE = float_info.mant_dig / C
+MIN_MACHINE_NUMBER = float_info.min
+MAX_MACHINE_NUMBER = float_info.max
+ZERO_MACHINE_ACCURACY = -mpmath.log(MIN_MACHINE_NUMBER, 10.0) + MACHINE_PRECISION_VALUE
+
+# backward compatibility
+machine_epsilon = MACHINE_EPSILON
def reconstruct_digits(bits) -> int:
@@ -118,13 +123,22 @@ def prec(dps) -> int:
return max(1, int(round((int(dps) + 1) * C)))
-def min_prec(*args):
- result = None
- for arg in args:
- prec = arg.get_precision()
- if result is None or (prec is not None and prec < result):
- result = prec
- return result
+def min_prec(*args: BaseElement) -> Optional[float]:
+ """
+ Returns the precision of the expression with the minimum precision.
+ If all the expressions are exact or non numeric, return None.
+
+ If one of the expressions is an inexact value with zero
+ nominal value, then its accuracy is used instead. For example,
+ ```min_prec(1, 0.``4) ``` returns 4.
+
+ Notice that this behaviour is different that the one obtained
+ using mathics.core.numbers.eval_Precision.
+ """
+ args_prec = (arg.get_precision() for arg in args)
+ return min(
+ (arg_prec for arg_prec in args_prec if arg_prec is not None), default=None
+ )
def pickle_mp(value):
diff --git a/mathics/core/parser/convert.py b/mathics/core/parser/convert.py
index 6fae9d1c5..7c6e5c0eb 100644
--- a/mathics/core/parser/convert.py
+++ b/mathics/core/parser/convert.py
@@ -83,14 +83,16 @@ def convert_Number(self, node: AST_Number) -> tuple:
if suffix is None:
# MachineReal/PrecisionReal is determined by number of digits
# in the mantissa
- d = len(man) - 2 # one less for decimal point
- if d < reconstruct_digits(machine_precision):
+ # if the number of digits is less than 17, then MachineReal is used.
+ # If more digits are provided, then PrecisionReal is used.
+ digits = len(man) - 2
+ if digits < reconstruct_digits(machine_precision):
return "MachineReal", sign * float(s)
else:
return (
"PrecisionReal",
("DecimalString", str("-" + s if sign == -1 else s)),
- d,
+ digits,
)
elif suffix == "":
return "MachineReal", sign * float(s)
@@ -118,10 +120,15 @@ def convert_Number(self, node: AST_Number) -> tuple:
# so ``` 0`3 === 0 ``` and ``` 0.`3 === 0.`4 ```
if node.value == "0":
return "Integer", 0
+
+ s_float = float(s)
+ prec = float(suffix)
+ if s_float == 0.0:
+ return "MachineReal", sign * s_float
return (
"PrecisionReal",
("DecimalString", str("-" + s if sign == -1 else s)),
- float(suffix),
+ prec,
)
# Put into standard form mantissa * base ^ n
diff --git a/mathics/eval/numbers.py b/mathics/eval/numbers.py
index 4e1db1dc3..69a8371c4 100644
--- a/mathics/eval/numbers.py
+++ b/mathics/eval/numbers.py
@@ -1,10 +1,109 @@
+from typing import Optional
+
+import mpmath
import sympy
+from mathics.core.atoms import Complex, MachineReal, PrecisionReal
from mathics.core.convert.sympy import from_sympy
+from mathics.core.element import BaseElement
from mathics.core.expression import Expression
+from mathics.core.number import MACHINE_PRECISION_VALUE, ZERO_MACHINE_ACCURACY, dps
from mathics.core.symbols import SymbolPlus
+def eval_Accuracy(z: BaseElement) -> Optional[float]:
+ """
+ Determine the accuracy of an expression expr.
+ If z is a Real value, returns a Python float value
+ representing the difference between the number of
+ significant decimal figures (Precision) and log_10(z).
+
+ For example,
+ ```
+ 12.345`2
+ ```
+ which is equivalent to 12.`2 has an accuracy of:
+ ```
+ 0.908509 == 2. - log(10, 12.345)
+ ```
+
+ If the expression contains Real values, returns
+ the minimal accuracy of all the numbers in the expression.
+
+ Otherwise returns None, representing infinite accuracy.
+ """
+ if isinstance(z, MachineReal):
+ if z.is_zero:
+ return ZERO_MACHINE_ACCURACY
+ z_f = z.to_python()
+ log10_z = mpmath.log((-z_f if z_f < 0 else z_f), 10)
+ return MACHINE_PRECISION_VALUE - log10_z
+
+ if isinstance(z, PrecisionReal):
+ if z.is_zero:
+ return float(dps(z.get_precision()))
+ z_f = z.to_python()
+ log10_z = mpmath.log((-z_f if z_f < 0 else z_f), 10)
+ return dps(z.get_precision()) - log10_z
+
+ if isinstance(z, Complex):
+ acc_real = eval_Accuracy(z.real)
+ acc_imag = eval_Accuracy(z.imag)
+ if acc_real is None:
+ return acc_imag
+ if acc_imag is None:
+ return acc_real
+ return min(acc_real, acc_imag)
+
+ if isinstance(z, Expression):
+ elem_accuracies = (eval_Accuracy(z_elem) for z_elem in z.elements)
+ return min((acc for acc in elem_accuracies if acc is not None), default=None)
+ return None
+
+
+def eval_Precision(z: BaseElement) -> Optional[float]:
+ """
+ Determine the precision of an expression expr.
+ If z is a Real value, returns the number of significant
+ decimal figures of z. For example,
+ ```
+ 12.345`2
+ ```
+ which is equivalent to 12.`2 has a precision of 2.
+
+ If the expression contains Real values, returns
+ the minimal accuracy of all the numbers in the expression.
+
+ If z is PrecisionReal(0.), the precision is 0. In that case,
+ the field "precision" is interpreted as "accuracy".
+
+ Otherwise returns None, representing infinite precision.
+ """
+
+ if isinstance(z, MachineReal):
+ return MACHINE_PRECISION_VALUE
+
+ if isinstance(z, PrecisionReal):
+ if z.is_zero:
+ return 0.0
+ return float(dps(z.get_precision()))
+
+ if isinstance(z, Complex):
+ prec_real = eval_Precision(z.real)
+ prec_imag = eval_Precision(z.imag)
+ if prec_real is None:
+ return prec_imag
+ if prec_imag is None:
+ return prec_real
+ return min(prec_real, prec_imag)
+
+ if isinstance(z, Expression):
+ elem_prec = (eval_Precision(z_elem) for z_elem in z.elements)
+ return min((prec for prec in elem_prec if prec is not None), default=None)
+
+ return None
+
+
def cancel(expr):
if expr.has_form("Plus", None):
return Expression(SymbolPlus, *[cancel(element) for element in expr.elements])
diff --git a/test/builtin/atomic/test_numbers.py b/test/builtin/atomic/test_numbers.py
index 9039abc63..da539617c 100644
--- a/test/builtin/atomic/test_numbers.py
+++ b/test/builtin/atomic/test_numbers.py
@@ -4,9 +4,15 @@
In particular, RealDigits[] and N[]
"""
-
from test.helper import check_evaluation
+import pytest
+
+from mathics.core.number import MACHINE_PRECISION_VALUE, ZERO_MACHINE_ACCURACY
+
+ZERO_MACHINE_ACCURACY_STR = str(ZERO_MACHINE_ACCURACY)
+DEFAUT_ACCURACY_10_STR = str(MACHINE_PRECISION_VALUE - 1)
+
def test_realdigits():
for str_expr, str_expected in (
@@ -177,43 +183,90 @@ def test_n():
check_evaluation(str_expr, str_expected)
-def test_accuracy():
- for str_expr, str_expected in (
- ("0`4", "0"),
- ("Accuracy[0.0]", "15."),
- ("Accuracy[0.000000000000000000000000000000000000]", "36."),
- ("Accuracy[-0.0]", "15."),
- # In WMA, this gives 36. Seems to be a rounding issue
- # ("Accuracy[-0.000000000000000000000000000000000000]", "36."),
- ("1.0000000000000000 // Accuracy", "15."),
- ("1.00000000000000000 // Accuracy", "17."),
- # Returns the accuracy of ```2.4```
- (" 0.4 + 2.4 I // Accuracy", "14.6198"),
- ("Accuracy[2 + 3 I]", "Infinity"),
- ('Accuracy["abc"]', "Infinity"),
+@pytest.mark.parametrize(
+ ("str_expr", "str_expected"),
+ [
+ # Accuracy for 0
+ ("0", "Infinity"),
+ ("0.", ZERO_MACHINE_ACCURACY_STR),
+ ("0.00", ZERO_MACHINE_ACCURACY_STR),
+ ("0.00`", ZERO_MACHINE_ACCURACY_STR),
+ ("0.00`2", ZERO_MACHINE_ACCURACY_STR),
+ ("0.00`20", ZERO_MACHINE_ACCURACY_STR),
+ ("0.00000000000000000000", "20."),
+ ("0.``2", "2."),
+ ("0.``20", "20."),
+ ("-0.`2", ZERO_MACHINE_ACCURACY_STR),
+ ("-0.`20", ZERO_MACHINE_ACCURACY_STR),
+ ("-0.``2", "2."),
+ ("-0.``20", "20."),
+ # Now for non-zero numbers
+ ("10", "Infinity"),
+ ("10.", DEFAUT_ACCURACY_10_STR),
+ ("10.00", DEFAUT_ACCURACY_10_STR),
+ ("10.00`", DEFAUT_ACCURACY_10_STR),
+ ("10.00`2", "1."),
+ ("10.00`20", "19."),
+ ("10.00000000000000000000", "20."),
+ ("10.``2", "2."),
+ ("10.``20", "20."),
+ # For some reason, the following test
+ # would fail in WMA
+ ("1. I", "Accuracy[1.]"),
+ (" 0.4 + 2.4 I", "Accuracy[2.4]"),
+ ("2 + 3 I", "Infinity"),
+ ('"abc"', "Infinity"),
# Returns the accuracy of ``` 3.2`3 ```
- ('Accuracy[F["a", 2, 3.2`3]]', "2.49482"),
- ('Accuracy[{{a, 2, 3.2`},{2.1`5, 3.2`3, "a"}}]', "2.49482"),
- # Another case of issues with rounding. In Mathics, this returns
- # 2.67776
- # ('Accuracy[{{a, 2, 3.2`},{2.1``3, 3.2``5, "a"}}]', '3.'),
- ):
- check_evaluation(str_expr, str_expected)
+ ('F["a", 2, 3.2`3]', "Accuracy[3.2`3]"),
+ ("F[1.3, Pi, A]", "15.8406"),
+ ('{{a, 2, 3.2`},{2.1`5, 3.2`3, "a"}}', "Accuracy[3.2`3]"),
+ ('{{a, 2, 3.2`},{2.1``3, 3.2``5, "a"}}', "Accuracy[2.1``3]"),
+ ("{1, 0.}", ZERO_MACHINE_ACCURACY_STR),
+ ("{1, 0.``5}", "5."),
+ ],
+)
+def test_accuracy(str_expr, str_expected):
+ check_evaluation(f"Accuracy[{str_expr}]", str_expected)
-def test_precision():
- for str_expr, str_expected in (
- ("0`4", "0"),
- ("Precision[0.0]", "MachinePrecision"),
- ("Precision[0.000000000000000000000000000000000000]", "0."),
- ("Precision[-0.0]", "MachinePrecision"),
- ("Precision[-0.000000000000000000000000000000000000]", "0."),
- ("1.0000000000000000 // Precision", "MachinePrecision"),
- ("1.00000000000000000 // Precision", "17."),
- (" 0.4 + 2.4 I // Precision", "MachinePrecision"),
- ("Precision[2 + 3 I]", "Infinity"),
- ('Precision["abc"]', "Infinity"),
- ('Precision[F["a", 2, 3.2`3]]', "3."),
- ('Precision[{{a,2,3.2`},{2.1`5, 2.`3, "a"}}]', "3."),
- ):
- check_evaluation(str_expr, str_expected)
+@pytest.mark.parametrize(
+ ("str_expr", "str_expected"),
+ [
+ # Precision for 0
+ ("0", "Infinity"),
+ ("0.", "MachinePrecision"),
+ ("0.00", "MachinePrecision"),
+ ("0.00`", "MachinePrecision"),
+ ("0.00`2", "MachinePrecision"),
+ ("0.00`20", "MachinePrecision"),
+ ("0.00000000000000000000", "0."),
+ ("0.``2", "0."),
+ ("0.``20", "0."),
+ ("-0.`2", "MachinePrecision"),
+ ("-0.`20", "MachinePrecision"),
+ ("-0.``2", "0."),
+ ("-0.``20", "0."),
+ # Now for non-zero numbers
+ ("10", "Infinity"),
+ ("10.", "MachinePrecision"),
+ ("10.00", "MachinePrecision"),
+ ("10.00`", "MachinePrecision"),
+ ("10.00`2", "2."),
+ ("10.00`20", "20."),
+ ("10.00000000000000000000", "21."),
+ ("10.``2", "3."),
+ ("10.``20", "21."),
+ # Returns the precision of ```2.4```
+ (" 0.4 + 2.4 I", "MachinePrecision"),
+ ("2 + 3 I", "Infinity"),
+ ('"abc"', "Infinity"),
+ # Returns the precision of ``` 3.2`3 ```
+ ('F["a", 2, 3.2`3]', "3."),
+ ('{{a, 2, 3.2`},{2.1`5, 3.2`3, "a"}}', "3."),
+ ('{{a, 2, 3.2`},{2.1``3, 3.2``5, "a"}}', "3."),
+ ("{1, 0.}", "MachinePrecision"),
+ ("{1, 0.``5}", "0."),
+ ],
+)
+def test_precision(str_expr, str_expected):
+ check_evaluation(f"Precision[{str_expr}]", str_expected)