• 大数计算:e^1000/300!


    1.问题:大数计算可能超出数据类型范围

    当单独计算 e^{1000},因为 e^{1000} > e^{700} \approx 1.01432e304,double的最大取值为1.79769e+308,所以 e^{1000} 肯定超过了double的表示范围。

    同样,对于300!也是如此。

    那我们应该怎么去计算和存储结果呢?

    2.解决方案

    从数学角度出发,对于超级大的数,运算方式、运算规律等肯定保持不变的

    很多时候,我们主要是利用相关的定理、公式进行简化或者极限处理。

    由于我项目里的精度要求不是很高,于是,可以采用相对宽松的方式解决这个问题:

    科学计数法

    3.代码实现

    1. /*
    2. * @file BigNumeric.hpp
    3. * @brief 模板类,用于大数计算,以科学计数法为实现形式,尾数部分有效位数12,指数部分完全准确.
    4. * 直接在工程中引入该hpp文件即可使用该类,重载了+-/*以及幂运算和开方等.
    5. * 由于指数部分采用的市 int ,故限制了本类使用时科学计数法的最大表示范围为:
    6. * a*10^2147483647 (-1.0<=a<=1.0)
    7. **
    8. * @author DongFengChuiLiu
    9. * @email 308021782@qq.com
    10. * @version 1.0
    11. * @date 2023/10/15
    12. */
    13. #ifndef __BigNumeric_h
    14. #define __BigNumeric_h
    15. #include
    16. #include
    17. template<typename _T>
    18. class BigNumeric
    19. {
    20. private:
    21. _T mantissa; //尾数
    22. int exponent; //指数
    23. public:
    24. BigNumeric(_T num)
    25. {
    26. if (num == (_T)0)
    27. {
    28. exponent = 0;
    29. mantissa = 0;
    30. }
    31. else
    32. {
    33. exponent = std::floor(std::log10(std::abs(num)));
    34. mantissa = num / std::pow(10.0, exponent);
    35. }
    36. }
    37. BigNumeric(_T mnt,int exp)
    38. {
    39. this->mantissa = mnt;
    40. this->exponent = exp;
    41. }
    42. ~BigNumeric() {}
    43. BigNumeric(const BigNumeric& other)
    44. {
    45. if (this == &other)
    46. return;
    47. this->exponent = other.exponent;
    48. this->mantissa = other.mantissa;
    49. }
    50. BigNumeric& operator=(const BigNumeric& other)
    51. {
    52. if(this == &other)
    53. return *this;
    54. this->exponent = other.exponent;
    55. this->mantissa = other.mantissa;
    56. return *this;
    57. }
    58. public:
    59. //取 BigNumeric 对象的实际值
    60. _T value()
    61. {
    62. if (this->exponent >= 308) return std::numeric_limits<_T>::infinity();
    63. return this->mantissa * std::pow(10.0, this->exponent);
    64. }
    65. //乘法运算
    66. // BigNumeric * BigNumeric
    67. BigNumeric operator*(const BigNumeric opr)
    68. {
    69. BigNumeric<_T> resmnt(this->mantissa * opr.mantissa);
    70. resmnt.exponent = CheckRange((_T)resmnt.exponent + this->exponent + opr.exponent);
    71. return resmnt;
    72. }
    73. // BigNumeric * _T
    74. BigNumeric operator*(const _T opr)
    75. {
    76. return *this * BigNumeric<_T>(opr);
    77. }
    78. //_T * BigNumeric
    79. friend BigNumeric operator*(const _T opr, BigNumeric opr1)
    80. {
    81. return opr1 * opr;
    82. }
    83. //除法
    84. // BigNumeric / BigNumeric
    85. BigNumeric operator/(const BigNumeric opr)
    86. {
    87. BigNumeric<_T> resmnt(this->mantissa / opr.mantissa);
    88. resmnt.exponent = CheckRange((_T)resmnt.exponent + this->exponent - opr.exponent);
    89. return resmnt;
    90. }
    91. // BigNumeric / _T
    92. BigNumeric operator/(const _T opr)
    93. {
    94. return *this / BigNumeric<_T>(opr);
    95. }
    96. // _T / BigNumeric
    97. friend BigNumeric operator/(const _T opr, const BigNumeric opr1)
    98. {
    99. return BigNumeric<_T>(opr) / opr1;
    100. }
    101. //加法
    102. // BigNumeric + BigNumeric
    103. BigNumeric operator+(const BigNumeric opr)
    104. {
    105. if (this->exponent - opr.exponent > 15) return *this;
    106. else if (this->exponent - opr.exponent < -15) return opr;
    107. int min = this->exponent > opr.exponent ? opr.exponent : this->exponent;
    108. BigNumeric<_T> resmnt(this->mantissa * std::pow(10.0, this->exponent - min) + opr.mantissa * std::pow(10.0, opr.exponent - min));
    109. resmnt.exponent = CheckRange((_T)resmnt.exponent + min);
    110. return resmnt;
    111. }
    112. // BigNumeric + _T
    113. BigNumeric operator+(const _T opr)
    114. {
    115. return *this + BigNumeric<_T>(opr);
    116. }
    117. // _T + BigNumeric
    118. friend BigNumeric operator+(const _T opr, BigNumeric opr1)
    119. {
    120. return opr1 + opr;
    121. }
    122. //减法
    123. // BigNumeric - BigNumeric
    124. BigNumeric operator-(const BigNumeric opr)
    125. {
    126. return *this + BigNumeric<_T>(opr) * (-1.0);
    127. }
    128. // BigNumeric - _T
    129. BigNumeric operator-(const _T opr)
    130. {
    131. return *this - BigNumeric<_T>(opr);
    132. }
    133. // _T - BigNumeric
    134. friend BigNumeric operator-(const _T opr, BigNumeric opr1)
    135. {
    136. return opr1 - opr;
    137. }
    138. //开方
    139. // BigNumeric^0.5
    140. BigNumeric Sqrt()
    141. {
    142. BigNumeric<_T> result(0.0);
    143. _T bgnmant = std::sqrt(this->mantissa);
    144. int bgnexp = this->exponent;
    145. if (bgnexp % 2 == 0)
    146. {
    147. result.mantissa = bgnmant;
    148. result.exponent = bgnexp / 2;
    149. }
    150. else
    151. {
    152. BigNumeric temp(bgnmant * std::sqrt(10.0));
    153. result.mantissa = temp.mantissa;
    154. result.exponent = temp.exponent + bgnexp / 2;
    155. }
    156. return result;
    157. }
    158. //幂
    159. // BigNumeric^_T
    160. BigNumeric Pow(_T exp)
    161. {
    162. BigNumeric temp(Vpow(this->mantissa, exp) * Vpow(10.0, this->exponent * (exp - (int)exp)));
    163. temp.exponent = CheckRange((_T)temp.exponent + (_T)this->exponent * exp);
    164. return temp;
    165. }
    166. public:
    167. //整数阶乘 int!
    168. static BigNumeric Factorial(const int opr)
    169. {
    170. if (opr < 0) return 1.0 / Factorial(-1.0 * opr + 1);
    171. else if (opr == 0) return BigNumeric(1.0);
    172. return Factorial(opr - 1) * opr;
    173. }
    174. //e指数幂运算 e^_T
    175. static BigNumeric Epow(const _T exp)
    176. {
    177. BigNumeric res(1.0);
    178. double e = 2.71828182845904523536;
    179. if (exp <= 700) return BigNumeric(std::pow(e, exp));
    180. int count = exp / 700;
    181. BigNumeric bgn(std::pow(e, 700));
    182. for (size_t i = 0; i < count; i++)
    183. res = res * bgn;
    184. BigNumeric bgn1(std::pow(e, exp - count * 700));
    185. res = res * bgn1;
    186. return res;
    187. }
    188. //e^BigNumeric
    189. static BigNumeric Epow(const BigNumeric opr)
    190. {
    191. BigNumeric res(1.0);
    192. double e = 2.71828182845904523536;
    193. BigNumeric mnt(std::pow(e, opr.mantissa));
    194. int count = std::abs(opr.exponent) > 1000 ? 1000 : std::abs(opr.exponent);
    195. if (count == 0) return mnt;
    196. if(opr.exponent < 0) res = mnt.Pow(0.1);
    197. else res = mnt.Pow(10.0);
    198. for (size_t i = 0; i < count - 1; i++)
    199. {
    200. if (opr.exponent < 0) res = res.Pow(0.1);
    201. else res = res.Pow(10.0);
    202. }
    203. return res;
    204. }
    205. //任意底数的幂运算 a^_T
    206. static BigNumeric Vpow(const _T e, const _T exp)
    207. {
    208. BigNumeric res(1.0);
    209. BigNumeric bgnmant(e);
    210. int chk = bgnmant.exponent == 0 ? exp : CheckRange(exp * bgnmant.exponent);
    211. if (chk <= 300) return BigNumeric(std::pow(e, exp));
    212. int count = exp / 300;
    213. BigNumeric bgn(std::pow(bgnmant.mantissa, 300));
    214. for (size_t i = 0; i < count; i++)
    215. {
    216. res = res * bgn;
    217. res.exponent = CheckRange(res.exponent + (_T)bgnmant.exponent * 300);
    218. }
    219. BigNumeric bgn1(std::pow(bgnmant.mantissa, exp - count * 300));
    220. res = res * bgn1;
    221. res.exponent = CheckRange(res.exponent + (_T)bgnmant.exponent * (exp - count * 300));
    222. return res;
    223. }
    224. private:
    225. //控制指数的值范围
    226. static int CheckRange(const _T num)
    227. {
    228. if (num >= (_T)2147483647) return 2147483647;
    229. else if( num <= (_T)-2147483648) return -2147483648;
    230. return num;
    231. }
    232. };
    233. #endif // !__BigNumeric_h

    4.测试

    1. //测试代码
    2. #include "BigNumeric.hpp"
    3. int main()
    4. {
    5. // 乘法测试
    6. /*BigNumeric bignum(2.13e303);
    7. BigNumeric bignum1(5e303);
    8. BigNumeric res = bignum * bignum1;
    9. res = bignum * 2;
    10. res = 2 * bignum;
    11. double t = res.value();*/
    12. // 除法测试
    13. /*BigNumeric bignum(2.13e303);
    14. BigNumeric bignum1(4.26e303);
    15. BigNumeric res = bignum1 / bignum;
    16. res = 240.0 / bignum;
    17. res = bignum / 2.13e302;*/
    18. //加法测试
    19. /*BigNumeric bignum(987654321);
    20. BigNumeric bignum1(9);
    21. BigNumeric res(bignum + bignum1);
    22. res = bignum + 9;
    23. res = 9 + bignum;*/
    24. //减法测试
    25. /*BigNumeric bignum(987654321);
    26. BigNumeric bignum1(9);
    27. BigNumeric res(bignum - bignum1);
    28. res = bignum - 9;
    29. res = 987654321 - bignum;*/
    30. //幂测试
    31. /*BigNumeric bignum(2.15 * 1.0e303);
    32. BigNumeric TMP = (bignum*54321).Sqrt();
    33. BigNumeric TMP1 = (bignum * 54321).Pow(0.5);
    34. bignum = BigNumeric(9.15);
    35. BigNumeric TMP2 = bignum.Pow(400);
    36. double t = TMP2.value();*/
    37. //阶乘测试
    38. BigNumeric<double> bignum = BigNumeric<double>::Factorial(300);
    39. BigNumeric<double> bignum1 = BigNumeric<double>::Epow(1000);
    40. bignum = bignum1 / bignum;
    41. double t = bignum.value();
    42. return 0;
    43. }

    结果:6.4369310844548986e-181,尾数部分有效位数为 12,指数部分完全准确。

  • 相关阅读:
    leetCode
    【博主推荐】SpringBoot API接口对数据库增删改查,路由,TOKEN,WebSocket完整版(附源码)
    Ag44团簇以及衍生团簇(银纳米团簇直径1-2nm)
    为什么都在说实时数据传输?
    基于 ApplicationEvent 实现事件监听(进阶篇)
    线程的概念及使用
    微信小程序 - - - - - 瀑布流效果实现
    TextView文字图片混排并添加点击事件监听,Textview里面的ImageSpan添加点击响应事件
    Oracle数据库从入门到精通系列之十二:段
    使用ChatGPT和MindShow一分钟生成PPT模板
  • 原文地址:https://blog.csdn.net/A_Pointer/article/details/133814946