階乗について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
天使

階乗について

#1

投稿記事 by 天使 » 16年前

1~入力された数字までの階乗を求めるプログラムを書こうと思っています。
添付したものはソースです。
最後に2つ追加したいです。
1つ目はもう一度計算するかをキーで入力させます。
2つ目はunsigned longで42憶程度まで計算できるようにしていますが
それを超えてしまった場合入力をやり直させるということです。

急いではいませんので質問が理解でき、お時間のある方のみで構いませんので
教えてください。

Dixq (管理人)

Re:階乗について

#2

投稿記事 by Dixq (管理人) » 16年前

同じ事を繰り返す時はループを使うといいです。

while(1){

    //計算;

    //キー入力

    if(キー == 続ける){
        continue;
    }
    else{
        break;
    }
}

こんな感じでどうでしょうか?
後、文字列はscanfじゃなくても読み取れます。
色んな実装方法があると思いますが、getcharを使ってこんなの書いてみました。

どうやったらキーボード入力からエラーなく効率よく取ってこれるのかイマイチよく知りませんが、
私の実装案です。
sizeof(4)のint型のキーボード入力された値をとってくる関数です。

(バグがあったため、次の投稿で提示します)

strtolを使うと、渡した文字列をint型に変換するとき、オーバーフローなら
( errno == ERANGE || errno == EINVAL ) でかつ
( *iVal == LONG_MIN || *iVal == LONG_MAX )
になることを利用して、エラーを判定しています。
また文字列に格納する時に、10億である10桁よりも多い数が入力された時点でエラーフラグを立てています。

格納したい変数のアドレスを渡します。
かえってきた値が0なら正常、1ならエラーです。
これで判定してみてはどうでしょう。
 

Dixq (管理人)

Re:階乗について

#3

投稿記事 by Dixq (管理人) » 16年前

unsigned int でしたね。ちょっと書き換えてサンプルにしました。
実行して確認して下さい。
#include <stdio.h>    //printf等の為に必要
#include <errno.h>    //errnoの為に必要
#include <stdlib.h>    //strtoulの為に必要
#include <ctype.h>    //isdigitの為に必要

/* キーボード入力された unsigned int 型の値を引数に格納する */
/* 関数値は 0:正常 1:異常 */
int GetUnsignedInt( unsigned int *iVal )
{
    int i=0, iCh;
    int iErrorFlag=0; /* エラーフラグ */
    int iSignFlag=0;    /* 符号フラグ  0: 1 文字目が数字、 1: 1 文字目が"+"、 2: 1 文字目が"-" */
    char cStr[12] = {0,}; /* キーボード入力された文字列を数値に変換する為の配列 */
    while ( ( iCh = getchar() ) != '\n' ) {
        /* 改行が行われるまで iCh に入力しながらループ */

        /* 1 文字目が符号ならそれによる数値を代入 */
        if( i == 0 ){
            if( iCh == '+' ){
                iSignFlag = 1;
            }
            else if( iCh == '-' ){
                iSignFlag = 2;
            }
        }
        if( ( iSignFlag ? i == 1 : i == 0 ) && ( iCh == '0' ) ) {
            /* 一桁目が 0 なら飛ばす */
            continue ;
        }
        if ( ( !( ( i == 0 ) && ( iCh == '+' || iCh == '-' ) ) && isdigit( iCh ) == 0 )
            || ( iSignFlag ? i >= 11 : i >= 10 )
            || iSignFlag == 2 ) {
            /* 
            最初に + か - である以外の文字で、10 進数で表せないか、
            最初に文字がある時は 11 文字以上、無い時は 10 文字以上であるか
            マイナスである時
            */
            iErrorFlag=1;
        } else if ( iCh != EOF ) {
            /* それ以外で EOF でなければ格納する */
            cStr[ i ] = (char) iCh;
        }
        i++;
    }
    /* エラーナンバーを 0 に設定 */
    errno = 0;
    /* cStr を10進数整数に変換する */
    *iVal = strtoul( cStr, NULL, 10 );
    if ( ( iErrorFlag == 1 )
        || ( ( errno == ERANGE || errno == EINVAL ) && ( *iVal == ULONG_MAX ) )
        || ( errno != 0 && *iVal == 0 ) ) {
        /* エラーフラグがオン、または
           errno が ERANGE か EINVAL で、かつ *iVal が ULONG_MAX ではない、または
           errno が 0 じゃなく、*iVal が 0 であるとき
           異常終了する */
        return 1;
    } else {
        /* 正常終了する */
        return 0;
    }
}
int main(void){
    signed int   iVal;
    unsigned int uiVal;
    if( GetUnsignedInt( &uiVal ) == 0 ){
        printf( "%u\n", uiVal );
    } else{
        printf( "エラー\n" );
    }
    return 0;
}

実行結果例1

3000000000
3000000000

実行結果例2
5000000000
エラー

実行結果例3
-3000000000
エラー

Dixq (管理人)

Re:階乗について

#4

投稿記事 by Dixq (管理人) » 16年前

int型を取ってくる関数にしてみました。
#include <stdio.h>    //printf等の為に必要
#include <errno.h>    //errnoの為に必要
#include <stdlib.h>    //strtoulの為に必要
#include <ctype.h>    //isdigitの為に必要

/* キーボード入力された int 型の値を引数に格納する */
/* 関数値は 0:正常 1:異常 */
int GetInt( int *iVal )
{
    int i=0, iCh;
    int iErrorFlag=0; /* エラーフラグ */
    int iSignFlag=0;    /* 符号フラグ  0: 1 文字目が数字、 1: 1 文字目が"+"、 2: 1 文字目が"-" */
    char cStr[12] = {0,}; /* キーボード入力された文字列を数値に変換する為の配列 */
    while ( ( iCh = getchar() ) != '\n' ) {
        /* 改行が行われるまで iCh に入力しながらループ */

        /* 1 文字目が符号ならそれによる数値を代入 */
        if( i == 0 ){
            if( iCh == '+' ){
                iSignFlag = 1;
            }
            else if( iCh == '-' ){
                iSignFlag = 2;
            }
        }
        if( ( iSignFlag ? i == 1 : i == 0 ) && ( iCh == '0' ) ) {
            /* 一桁目が 0 なら飛ばす */
            continue ;
        }
        if ( ( !( ( i == 0 ) && ( iCh == '+' || iCh == '-' ) ) && isdigit( iCh ) == 0 )
            || ( iSignFlag ? i >= 11 : i >= 10 )) {
            /* 
            最初に + か - である以外の文字で、10 進数で表せないか、
            最初に文字がある時は 11 文字以上、無い時は 10 文字以上であるとき
            */
            iErrorFlag=1;
        } else if ( iCh != EOF ) {
            /* それ以外で EOF でなければ格納する */
            cStr[ i ] = (char) iCh;
        }
        i++;
    }
    /* エラーナンバーを 0 に設定 */
    errno = 0;
    /* cStr を10進数整数に変換する */
    *iVal = strtol( cStr, NULL, 10 );

    if ( ( iErrorFlag == 1 )
        || ( ( errno == ERANGE || errno == EINVAL ) && ( *iVal == LONG_MIN || *iVal == LONG_MAX ) )
        || ( errno != 0 && *iVal == 0 ) ) {
        /* エラーフラグがオン、または
           errno が ERANGE か EINVAL で、かつ *iVal == LONG_MIN か *iVal == LONG_MAX であるときか、
           errno が 0 じゃなく、*iVal が 0 であるとき
           異常終了する */
        return 1;
    } else {
        /* 正常終了する */
        return 0;
    }
}
int main(void){
    int   iVal;
    if( GetInt( &iVal ) == 0 ){
        printf( "%d\n", iVal );
    } else{
        printf( "エラー\n" );
    }
    return 0;
}


実行結果例1

2000000000
2000000000

実行結果例2

3000000000
エラー

実行結果例3

-3000s000000
エラー

実行結果例4

-0000002000000000
-2000000000
 

non

Re:階乗について

#5

投稿記事 by non » 16年前

>printf("階乗結果は %d です。\n\n",Sum);

unsugned long なので %lu です。
また、staticにする意味はないと思います。

仮にunsigned long の最大値が 4294967295だとすると、12の階乗までしか求められないですね。
12までってプログラムしちゃいけないんですね。
計算されて出てきた数をその時、かけた数で割ってみて、もとの数と同じかチェックしてみたらいかがでしょう。違っていたら、オーバーフローしていたということになりませんかね。

天使

Re:階乗について

#6

投稿記事 by 天使 » 16年前

getcharを使っていたのですが文字列ではなく数値を取り出しているのでscanfを使っています。
正直、両方の使い方がいまいちわからないのですが
getcharはchar型の文字列や文字
scanfは数値
と考えていいのでしょうか?

%luについて
すいませんwww
はじめintでしてたので直していませんでしたw

閉鎖

“C言語何でも質問掲示板” へ戻る