質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

6回答

9388閲覧

C:大きな数の計算方法,オーバーフロー回避

for

総合スコア6

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2017/05/29 05:08

C言語で3^80の計算をしたいのですが、数が大きすぎてオーバーフローしてしまいます。各桁ごとに配列を置けばいいのかとも思いましたが、いまいちよくわかりません。
解決方法が分かる方いらっしゃいましたらよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

多倍長整数演算のライブラリを使うのも一つの手段です。
たとえば、多倍長整数演算ライブラリ (GNU MP)などを参考にしてください。

「多倍長演算 Cライブラリ」でググればいくつか出てくると思います。

投稿2017/05/29 09:38

PineMatsu

総合スコア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

raccy

総合スコア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

naomi3

総合スコア1105

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

計算結果を文字列に直すんだろうと思いますが、定数ならあらかじめ計算してデータとして持っておけばいいと思います。

投稿2017/05/29 05:22

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

計算結果をどのように利用するかにもよりますが、
そんなサンプルプログラムはたくさんネットで出ています。

【参考URL】
http://7ujm.net/play/dcalc.html

投稿2017/05/29 05:12

s.t.

総合スコア2021

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

違う観点から回答します。

UNIX/Linux系の環境であれば、
「bc」コマンドを呼び出す手もあります。

投稿2017/06/02 11:56

ai_2013_dev

総合スコア338

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問