What's wrong
The overloads that take no precision (Sin(x), Cos(x), SinCos(x), Tan(x), in PreciseNumber/PreciseNumber.Trigonometry.cs:101-102, 129-130, 157-158, 220-221) use DefaultTrigonometricPrecision(x) (line 736), which returns max(x.SignificantDigits, 50).
The built-in constants carry ConstantPrecision (150) digits and have one integer digit. For them the default asks for 150 digits, and reducing a one-integer-digit argument to 150 digits needs π to 151 digits. RequireReducibleArgument then throws. In other words, the overload that picks its own precision picks one it will refuse.
This contradicts two pieces of documentation:
- The XML docs for these overloads promise the result is "produced to the significant digits of x" and list no
ArgumentOutOfRangeException.
- The remark on
DefaultTrigonometricPrecision says it exists so that a constant carrying ConstantPrecision digits is handled.
Reproduction
Sin(PreciseNumber.Pi) → ArgumentOutOfRangeException: Reducing an argument of 1 integer digits to 150 significant digits needs π to 151 digits, but it is stored to 150. At most 149 …
Cos(Pi), Cos(Tau), Sin(E), SinCos(Pi) → same exception
Sin(Ln2) → works (0 integer digits)
Exp(Pi), Log(Pi), Pow(Pi, E), Sinh(Pi), Atan(Pi), Atan2(Pi, E), Asin(Pi/4) → all work at 150 digits
The same exception is thrown for any computed value that has at least 150 significant digits and at least one integer digit, for example a quotient or root of two constants.
Why it matters
Math.Sin(Math.PI)-style code is the most basic use of the API, and every other transcendental function handles these inputs. Callers can't predict which values will throw without knowing the internal ceiling on π digits.
Suggested fix
Related: Tan's guard digits make the ceiling even lower for Tan, which I'm filing separately.
What's wrong
The overloads that take no precision (
Sin(x),Cos(x),SinCos(x),Tan(x), inPreciseNumber/PreciseNumber.Trigonometry.cs:101-102, 129-130, 157-158, 220-221) useDefaultTrigonometricPrecision(x)(line 736), which returnsmax(x.SignificantDigits, 50).The built-in constants carry
ConstantPrecision(150) digits and have one integer digit. For them the default asks for 150 digits, and reducing a one-integer-digit argument to 150 digits needs π to 151 digits.RequireReducibleArgumentthen throws. In other words, the overload that picks its own precision picks one it will refuse.This contradicts two pieces of documentation:
ArgumentOutOfRangeException.DefaultTrigonometricPrecisionsays it exists so that a constant carryingConstantPrecisiondigits is handled.Reproduction
The same exception is thrown for any computed value that has at least 150 significant digits and at least one integer digit, for example a quotient or root of two constants.
Why it matters
Math.Sin(Math.PI)-style code is the most basic use of the API, and every other transcendental function handles these inputs. Callers can't predict which values will throw without knowing the internal ceiling on π digits.Suggested fix
Math.Min(DefaultTrigonometricPrecision(x), ConstantPrecision - IntegerDigitCount(x)), with a floor of 1.1e200, which the existing refusal test already pins.(x, significantDigits)overloads keep their current strict behaviour, as intended by Trig/exp/log functions silently return under-precision (or wrong) digits once required working precision exceeds the 150-digit π/e/ln constants #96/Refuse a sine whose reduction needs more of π than exists [patch] #97.Sin(Pi),Cos(Tau),SinCos(Pi)andTan(Pi / 4)return values and do not throw.Related:
Tan's guard digits make the ceiling even lower forTan, which I'm filing separately.