소수점을 소수점의 힘으로 키우고 있습니까?
-
06-07-2019 - |
문제
.NET Framework는 Math Class A 메소드에서 두 배의 전원을 공급합니다. 그러나 정밀한 요구 사항에 따라 소수점에서 10 진수 전력을 높여야합니다 [Pow (10 진수 A, 소수점 B)]. 프레임 워크에 그러한 기능이 있습니까? 이런 종류의 기능을 가진 라이브러리를 아는 사람이 있습니까?
해결책
내 문제를 해결하기 위해 나는 일부를 찾았습니다 확장 시리즈, 그리고 나는 그것들이 방정식 x^n = e^(n * ln x)를 해결하기 위해 구현했습니다.
// Adjust this to modify the precision
public const int ITERATIONS = 27;
// power series
public static decimal DecimalExp(decimal power)
{
int iteration = ITERATIONS;
decimal result = 1;
while (iteration > 0)
{
fatorial = Factorial(iteration);
result += Pow(power, iteration) / fatorial;
iteration--;
}
return result;
}
// natural logarithm series
public static decimal LogN(decimal number)
{
decimal aux = (number - 1);
decimal result = 0;
int iteration = ITERATIONS;
while (iteration > 0)
{
result += Pow(aux, iteration) / iteration;
iteration--;
}
return result;
}
// example
void main(string[] args)
{
decimal baseValue = 1.75M;
decimal expValue = 1/252M;
decimal result = DecimalExp(expValue * LogN(baseValue));
}
Pow () 및 factorial () 함수는 전원이 항상 int (내부 DE 파워 시리즈)이기 때문에 간단합니다.
다른 팁
이것은 긍정적 인 정수 지수와 소수점베이스에서 가장 빠릅니다.
// From http://www.daimi.au.dk/~ivan/FastExpproject.pdf
// Left to Right Binary Exponentiation
public static decimal Pow(decimal x, uint y){
decimal A = 1m;
BitArray e = new BitArray(BitConverter.GetBytes(y));
int t = e.Count;
for (int i = t-1; i >= 0; --i) {
A *= A;
if (e[i] == true) {
A *= x;
}
}
return A;
}
다음은 Math.pow ()를 .NET의 Double 기반 구현보다 정밀도로 수동으로 구현하는 C# 프로그램입니다. LinqPad로 자르고 붙여 넣어 즉시 실행하거나 .dump ()을 Console.writelines로 변경하십시오.
결과 테스트를 포함 시켰습니다. 테스트는 다음과 같습니다.
- 목표 = .4% PA와 함께 매일 10,000
- 답 =는 10 040이어야합니다
- 어떻게 = 소수 b = 10000; for (int i = 0; i <365; i ++) {b *= rate; } where rate = (1.004)^(1/365)
3 개의 속도 구현을 테스트했습니다. (1) 수동 계산 (2) Excel (3) Math.pow
수동 계산은 정확도가 가장 높습니다. 결과는 다음과 같습니다.
Manually calculated rate: 1.0000109371043837652682334292
Excel rate: 1.000010937104383712500000M [see formula =(1.004)^(1/365)]
Math.Pow rate: 1.00001093710438
Manual - .4%pa on R10,000: 10040.000000000000000000000131
Excel - .4%pa on R10,000: 10039.999999999806627646709094
Math.Pow - .4%pa on R10,000:10039.999999986201948942509648
나는 또한 거기에 몇 가지 추가 작업을 남겼습니다. 나는 가장 높은 요인이 무엇인지 확립하는 데 사용했습니다 (= 22).
LinqPad 코드 :
/*
a^b = exp(b * ln(a))
ln(a) = log(1-x) = - x - x^2/2 - x^3/3 - ... (where |x| < 1)
x: a = 1-x => x = 1-a = 1 - 1.004 = -.004
y = b * ln(a)
exp(y) = 1 + y + y^2/2 + x^3/3! + y^4/4! + y^5/5! + ...
n! = 1 * 2 * ... * n
*/
/*
//
// Example: .4%pa on R10,000 with daily compounding
//
Manually calculated rate: 1.0000109371043837652682334292
Excel rate: 1.000010937104383712500000M =(1.004)^(1/365)
Math.Pow rate: 1.00001093710438
Manual - .4%pa on R10,000: 10040.000000000000000000000131
Excel - .4%pa on R10,000: 10039.999999999806627646709094
Math.Pow - .4%pa on R10,000:10039.999999986201948942509648
*/
static uint _LOOPS = 10; // Max = 22, no improvement in accuracy after 10 in this example scenario
// 8: 1.0000109371043837652682333497
// 9: 1.0000109371043837652682334295
// 10: 1.0000109371043837652682334292
// ...
// 21: 1.0000109371043837652682334292
// 22: 1.0000109371043837652682334292
// http://www.daimi.au.dk/~ivan/FastExpproject.pdf
// Left to Right Binary Exponentiation
public static decimal Pow(decimal x, uint y)
{
if (y == 1)
return x;
decimal A = 1m;
BitArray e = new BitArray(BitConverter.GetBytes(y));
int t = e.Count;
for (int i = t-1; i >= 0; --i) {
A *= A;
if (e[i] == true) {
A *= x;
}
}
return A;
}
// http://stackoverflow.com/questions/429165/raising-a-decimal-to-a-power-of-decimal
// natural logarithm series
public static decimal ln(decimal a)
{
/*
ln(a) = log(1-x) = - x - x^2/2 - x^3/3 - ... (where |x| < 1)
x: a = 1-x => x = 1-a = 1 - 1.004 = -.004
*/
decimal x = 1 - a;
if (Math.Abs(x) >= 1)
throw new Exception("must be 0 < a < 2");
decimal result = 0;
uint iteration = _LOOPS;
while (iteration > 0)
{
result -= Pow(x, iteration) / iteration;
iteration--;
}
return result;
}
public static ulong[] Fact = new ulong[] {
1L,
1L * 2,
1L * 2 * 3,
1L * 2 * 3 * 4,
1L * 2 * 3 * 4 * 5,
1L * 2 * 3 * 4 * 5 * 6,
1L * 2 * 3 * 4 * 5 * 6 * 7,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20,
14197454024290336768L, //1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21, // NOTE: Overflow during compilation
17196083355034583040L, //1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22 // NOTE: Overflow during compilation
};
// http://stackoverflow.com/questions/429165/raising-a-decimal-to-a-power-of-decimal
// power series
public static decimal exp(decimal y)
{
/*
exp(y) = 1 + y + y^2/2 + x^3/3! + y^4/4! + y^5/5! + ...
*/
uint iteration = _LOOPS;
decimal result = 1;
while (iteration > 0)
{
//uint fatorial = Factorial(iteration);
ulong fatorial = Fact[iteration-1];
result += (Pow(y, iteration) / fatorial);
iteration--;
}
return result;
}
void Main()
{
decimal a = 1.004M;
decimal b = 1/365M;
decimal _ln = ln(a);
decimal y = b * _ln;
decimal result = exp(y);
result.Dump("Manual rate");
decimal excel = 1.000010937104383712500000M; // =(1.004)^(1/365)
excel.Dump("Excel rate");
decimal m = (decimal)Math.Pow((double)a,(double)b);
m.Dump("Math.Pow rate");
//(result - excel).Dump("Diff: Manual - Excel");
//(m - excel).Dump("Diff: Math.Pow - Excel");
var f = new DateTime(2013,1,1);
var t = new DateTime(2014,1,1);
Test(f, t, 10000, result, "Manual - .4%pa on R10,000");
Test(f, t, 10000, excel, "Excel - .4%pa on R10,000");
Test(f, t, 10000, m, "Math.Pow - .4%pa on R10,000");
}
decimal Test(DateTime f, DateTime t, decimal balance, decimal rate, string whichRate)
{
int numInterveningDays = (t.Date - f.Date).Days;
var value = balance;
for (int i = 0; i < numInterveningDays; ++i)
{
value *= rate;
}
value.Dump(whichRate);
return value - balance;
}
/*
// Other workings:
//
// Determine maximum Factorial for use in ln(a)
//
ulong max = 9,223,372,036,854,775,807 * 2 // see http://msdn.microsoft.com/en-us/library/ctetwysk.aspx
Factorial 21 = 14,197,454,024,290,336,768
Factorial 22 = 17,196,083,355,034,583,040
Factorial 23 = 8,128,291,617,894,825,984 (Overflow)
public static uint Factorial_uint(uint i)
{
// n! = 1 * 2 * ... * n
uint n = i;
while (--i > 1)
{
n *= i;
}
return n;
}
public static ulong Factorial_ulong(uint i)
{
// n! = 1 * 2 * ... * n
ulong n = i;
while (--i > 1)
{
n *= i;
}
return n;
}
void Main()
{
// Check max ulong Factorial
ulong prev = 0;
for (uint i = 1; i < 24; ++i)
{
ulong cur = Factorial_ulong(i);
cur.Dump(i.ToString());
if (cur < prev)
{
throw new Exception("Overflow");
}
prev = cur;
}
}
*/
나는 그것이 당신이 플러그를 연결하려는 숫자에 크게 의존한다고 생각합니다. 'a'와 'b'가 'nice'숫자가 아니라면, 저장할 수없는 말이되지 않는 값을 얻을 수 있으며 c#. Bigdecimal은 Java Bigdecimal처럼 전혀 행동합니다. 아마도 그러한 경우에는 예외가 발생할 것입니다.
실제로 이것을하고 싶습니까? ㅏ decimal
곱하기는보다 약 40 배 더 느립니다 double
S, 그래서 나는 소수점을 기대할 것입니다 Math.Pow()
실제로 사용할 수 없습니다.
그러나 정수 전력 만 기대한다면 이미 여기에서 논의 된 정수 기반 전력 알고리즘을 사용하는 것이 좋습니다.