C言語で3^80の計算をしたいのですが、数が大きすぎてオーバーフローしてしまいます。各桁ごとに配列を置けばいいのかとも思いましたが、いまいちよくわかりません。
解決方法が分かる方いらっしゃいましたらよろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
多倍長整数演算のライブラリを使うのも一つの手段です。
たとえば、多倍長整数演算ライブラリ (GNU MP)などを参考にしてください。
「多倍長演算 Cライブラリ」でググればいくつか出てくると思います。
投稿2017/05/29 09:38
総合スコア3579
0
GCCであれば拡張で__int128
を使えます。3^80 < 2^127-1なのでそのまま計算できます。ただし、__int128
はprintfでそのまま表示できないため、表示が必要な場合はそこも作り込む必要があります。
C
1#include <stdio.h> 2#include <stdbool.h> 3 4#define INT10_19 10000000000000000000ULL 5 6static inline void print_int128_in(__int128 n) { 7 __int128 d = n / INT10_19; 8 unsigned long long m = (unsigned long long)(n % INT10_19); 9 if (d != 0) { 10 print_int128_in(d); 11 printf("%019llu", m); 12 } else { 13 printf("%llu", m); 14 } 15} 16 17static inline void print_int128(__int128 n) 18{ 19 if (n == 0) { 20 printf("%d", 0); 21 return; 22 } else if (n < 0) { 23 printf("-"); 24 n *= -1; 25 } 26 print_int128_in(n); 27} 28 29 30int main(void) 31{ 32 __int128 x = 1; 33 __int128 b = 3; 34 int e = 80; 35 while (true) { 36 if (e % 2) { 37 x *= b; 38 } 39 e /= 2; 40 if (e == 0) { 41 break; 42 } 43 b *= b; 44 } 45 print_int128(x); 46 printf("\n"); 47}
コンパイル時はgcc -std=gnu11
等とGNU拡張を有効にしてください。冪乗計算は2乗したものを掛けていくという方法である程度高速化しています。なお、x86_64のCPUでは、128bit整数なんてものを扱える命令は存在しないため、__int128
をhighとlowにそれぞれわけて計算しており、通常の整数値演算よりも遅いです。
投稿2017/06/02 23:09
総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
log(3^80)/log(256)≒15.85なので、16バイトあれば十分に表現できることがわかります。
乗数(この場合は3)と限られた小さな値の場合、乗算は比較的簡単にできます。
確認のため、3^1, 3^2…と順に十進化および表示をしていますが、3^80だけ十進化および表示をすれば、速くなりますね。
1桁をunsigned charとしましたが、unsigned shortやunsigned longにすれば、さらに高速化が計れます。
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5 6#define TRUE (0 == 0) 7#define FALSE (0 != 0) 8#define FIGURES 16 9#define DECIMAL_FIGURES (1 + (int)(8 * FIGURES * 0.30102999566398119521373889472449)) 10 11 12/* 13 * 掛け算をする 14 * 戻り値:繰上げ 15 * product:積 (要素FIGURESの配列) 16 * multiplicand:被乗数 (要素FIGURESの配列) 17 * multiplier:乗数 18 */ 19unsigned char multiply(unsigned char *product, unsigned char *multiplicand, unsigned char multiplier) 20{ 21 int i; 22 unsigned char carry = 0; 23 24 for (i = 0; i < FIGURES; i++) { 25 unsigned short sh_product = (unsigned short)multiplicand[i] * (unsigned short)multiplier + carry; 26 product[i] = (unsigned char)sh_product; 27 carry = (unsigned char)(sh_product >> 8); 28 } 29 30 return carry; 31} 32 33 34/* 35 * 割り算をする 36 * 戻り値:余り 37 * quotient:商 (要素FIGURESの配列) 38 * dividend:被除数 (要素FIGURESの配列) 39 * divisor:除数 40 */ 41unsigned char divide(unsigned char *quotient, unsigned char *dividend, unsigned char divisor) 42{ 43 int i; 44 unsigned short remainder = 0; 45 46 for (i = FIGURES - 1; i >= 0; i--) { 47 unsigned short sh_dividend = (remainder << 8) + (unsigned short)dividend[i]; 48 quotient[i] = (unsigned char)(sh_dividend / divisor); 49 remainder = sh_dividend % divisor; 50 } 51 52 return (unsigned char)remainder; 53} 54 55 56/* 57 * 数が0か否かを判定する 58 * 戻り値:TRUE:0/FALSE:0以外 59 * digit:数 (要素FIGURESの配列) 60 */ 61int is_zero(unsigned char *digit) 62{ 63 int i; 64 65 for (i = 0; i < FIGURES; i++) { 66 if (digit[i] != 0) { 67 return FALSE; 68 } 69 } 70 71 return TRUE; 72} 73 74 75/* 76 * 数を十進法文字列に変換する 77 * 戻り値:decimal_stringと同じ 78 * decimal_string:十分な領域がある文字列バッファ 79 * digit:数 (要素FIGURESの配列) 80 */ 81char *to_decimal(char *decimal_string, unsigned char *digit) 82{ 83 unsigned char dividend[FIGURES]; 84 unsigned char remainder; 85 char decimal[2]; 86 int length = 0; 87 88 memcpy(dividend, digit, FIGURES); 89 decimal_string[0] = '\0'; 90 91 do { 92 remainder = divide(dividend, dividend, 10); 93 sprintf(decimal, "%u", remainder); 94 95 /* 1文字をdecimal_stringの左に追加する */ 96 memmove(& decimal_string[1], & decimal_string[0], ++length); 97 decimal_string[0] = decimal[0]; 98 } while (! is_zero(dividend)); 99 100 return decimal_string; 101} 102 103 104int main(void) 105{ 106 int exponential; 107 unsigned char product[FIGURES] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* = 1 */ 108 char decimal_string[DECIMAL_FIGURES + 1]; 109 110 for (exponential = 1; exponential <= 80; exponential++) { 111 printf("3^%2d = ", exponential); 112 if (multiply(product, product, 3) > 0) { 113 printf("overflow!\n"); 114 return 1; 115 } 116 printf("%s\n", to_decimal(decimal_string, product)); 117 } 118 119 return 0; 120}
投稿2017/06/02 15:51
総合スコア1105
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
計算結果をどのように利用するかにもよりますが、
そんなサンプルプログラムはたくさんネットで出ています。
【参考URL】
http://7ujm.net/play/dcalc.html
投稿2017/05/29 05:12
総合スコア2021
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
違う観点から回答します。
UNIX/Linux系の環境であれば、
「bc」コマンドを呼び出す手もあります。
投稿2017/06/02 11:56
総合スコア338
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。