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

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

ただいまの
回答率

89.12%

実数型(double)を使用せずに浮動小数点の計算を行う

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 6,486

kuma_dansyaku

score 18

 はじめに

いつもお世話になっています。

表題について質問があります。
組込みC(C99)でdouble型を使わずに小数点の計算を行いたいのですが可能でしょうか?
取りあえずVisual Studioで以下の様なソースコードを作ってみましたがやはり端数処理が上手くいきません。

#include <stdio.h>

#define MILLION_TIMES (1000000)

int main(char argc, char *argv[]) {

    int dividend;    // 被除数
    int divisor;    // 除数(0は絶対に使わない)

    dividend =  87;
    divisor = 31;

    // 確認用です(実数型は使わない)
    double dDiv = (double)dividend / divisor;


    int integer = dividend / divisor;
    int idec = ((dividend * MILLION_TIMES) / divisor)-(integer * MILLION_TIMES) ;


    // 商に除数をかければ被除数になる
    printf("[double]\n");
    printf("%d / %d =%lf\n", dividend, divisor, dDiv);
    printf("%f * %d =%lf\n", dDiv, divisor, dDiv*divisor);
    printf("\n");
    printf("[int]\n");
    printf("%d / %d =%d.%06d\n", dividend, divisor, integer, idec);
    printf("%d.%d * %d =%d\n", integer, idec, divisor, divisor*integer+(divisor*idec)/ MILLION_TIMES);

    return 0;
}

[double]
87 / 31 =2.806452
2.806452 * 31 =87.000000

[int]
87 / 31 =2.806451
2.806451 * 31 =86

これ以外にも負数の場合の動作も考える必要があって悩んでいます。
これ以上は無理なのでしょうか?

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2017/05/18 16:32

    除算アルゴリズムは色々ありますが、double を使わないのは何のためですか?

    キャンセル

  • kuma_dansyaku

    2017/05/18 17:26

    組み込みなので使えないのです。正確には使えないわけでは無いですが、使うとめちゃめちゃ処理が遅くなるので使えません。あと使うとレビューで怒られます

    キャンセル

  • Zuishin

    2017/05/18 17:35

    つまり、組み込みの double より高速で同程度の精度を持った除算関数を作らなければならないということですか?

    キャンセル

  • kuma_dansyaku

    2017/05/18 22:45

    そうですね。検討用なのでそれ程重要度は高くないですが自分で調べていて限界だったのでヒントでも頂ければとこの質問をしました

    キャンセル

回答 5

+6

さて、どのような理由でdoubleを使わずにどんな計算を行いたいのでしょうか。やりたいことによって、適切なモデルは変わってきます。

  • ネイティブで浮動小数点演算のできない環境向けにコードを書く
  • 精度は犠牲にしていいから、できるだけ高速に計算したい
  • 誤差なく有理数として計算を行いたい
  • 通貨計算なので、浮動小数点演算で生じる小数の誤差が問題になる

その事情がわからないと、不適切な計算モデルを提案されることになります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/18 23:12

    画像縮小です。
    1byte/1pixの画像(色の格納方法は話がややこしくなるので考え無いでください)
    縮小サイズからベース画像の位置を割り出しそこから上下左右の色情報を取って中間色を得ると言う事をしたいのです。

    実際やるなら潤沢なAPI(メソッド)を持った言語でやるか、専用のライブラリを用意する必要があるのですが、何も無いこの状況で実装してくれと言われたので右往左往しています。

    > ネイティブで浮動小数点演算のできない環境向けにコードを書く
    →マイコンに焼くのでその通りです
    > 精度は犠牲にしていいから、できるだけ高速に計算したい
    →精度の方が重要です

    取りあえず小数の計算が出来れば良いと思っていたのと、仕事の話に密になるので多く書けなかったので、情報が少なすぎると思われましたらすみませんでした。

    キャンセル

checkベストアンサー

+5

こんにちは。

組込みの場合は、浮動小数点計算負荷が許容できないケースが多いですね。
そのような時は、固定小数点を良く使います。掲示されてるソースは正に固定小数点処理です。

端数がうまくあわないのは丸め処理の問題です。0.999999999でも切り捨てると0です。本来丸めて1にしたいところです。浮動小数点処理はこのような丸め処理をしています。

divisor*integer+(divisor*idec)/ MILLION_TIMES

は、下記処理でお手軽な四捨五入になります。

(divisor*integer+(divisor*idec) + MILLION_TIMES/2)/ MILLION_TIMES

負の数では妥当ではない気もしますので、良く検討下さい。
(負の数の四捨五入を考え出すと、丸め処理は様々な考え方があるので頭痛いです。)


【追記】
書こうと思いつつ忘れてました。
小数点位置を10進数で決めると計算が重くなるし、オーバーフローするしないの桁数が曖昧になるので、2進数で決めると良いです。固定小数点処理をシフト演算で済ませられるので高速なのです。なれない2進数で検討するのは少したいへんですが、10進数変換誤差がない分 楽ですよ。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/18 23:03

    レスありがとうございます。
    具体的な手法まで書いて頂き参考になります。

    あの後聞いたところunsinged(符号なし) との事なので負数を使う必要は無いとの事なので検討範囲は狭まりました。

    上記のコメントにも記載いたしましたが、ソフト処理で画像の縮小を行ってほしいと言われバイニリア、(出来れば)バイキュービックも試して欲しいと言われ「冗談でしょ?なんちゃってバイニリアしか出来ませんよ?」と思いながら色々試していました。

    ご提示頂いた2進数で扱う方法(考え方)は初めて知りました。
    この辺は皆様から頂いた用語を元に調べてみようとおもいます。

    キャンセル

  • 2017/05/19 06:02

    コメント欄で失礼。私も固定小数点が良いと思います。
    初代プレステが固定小数点だったことを思いながら取り組むと捗るのではないでしょうか。

    固定小数点では浮動小数点よりもサイズ、拡縮度合いが厳密に制限されるので注意してください。
    仕様として例えば「1/64より小さい縮小はできない」とか、「2048px以上の画像は扱えない」といった制限をあらかじめ設ける必要があります。

    キャンセル

+1

全くdoubleやfloat型を使わないのは難しいでしょうね。

組み込みのCPUで浮動小数点演算がソフトウェアの処理の場合は、速度的に速く処理したい場合に限って、全てを整数で演算して処理することはあります。その場合は、何倍するのかは数値が取りうる範囲と有効桁数をよくよく吟味した上でその処理に特化した書き方をしています。オーバーフローやアンダーフローにも注意しながらの処理なので慎重に設計しなくてはいけません。(倍数は桁取りが楽になる様に10000倍とか100000倍とかの10のn乗を使います)

桁を大きく取りたい場合は、多倍長演算のライブラリを用いる手もあります。

PC上で動かすソフトの場合は、こういうことはまず考えませんね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/18 22:56

    レスありがとうございます。

    >PC上で動かすソフトの場合は、こういうことはまず考えませんね。
    そうなんですよ。言われた時目が点になりました。
    画像の縮小処理でバイリニア手法を使うに当たってこの難題にぶち当たりました。
    ここまで行くとコンバートになるのでこう言うのはハードの方でやってくれれば良いのに・・・・。

    そんな事を思いながらクイズ感覚で色々試しています。
    ここまでやって検討用のお試し処理なので実採用はしてもらえないですが自分の知識になればこれも良い経験かと思い頑張っていきます。
    ありがとうございました。

    キャンセル

+1

wikipedia

Chironianさんの言うとおり2進数(小数部分を何ビットとるか)で考えるのがミソのようです。

英語の方だとQフォーマットにもう少し詳しく書かれていました。
https://en.wikipedia.org/wiki/Q_(number_format)

以下 チラシの裏。
昔FPUのないpowerpcでカーネルのFPUエミュレーションが激遅なのが問題になって、GCCの4.3(だったかな?)で固定小数点型(_Fract)というのを使ってみようとしたけどコンパイルできなくて結局soft-float使ったのを思い出しました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/18 22:51

    レスありがとうございます。
    リンク先参考にさせていただきます。

    今時の組込みチップなのでFPUはあるはずなのですがそんな無理難題を言われました。
    浮動小数点演算ライブラリと言うのがあるんですね。
    こっちで使っているコンパイラはなんだったかな?ARMかなんかだったと思いますが、下っ端のペーペーには仕様を流してくれないので言われた情報だけでやっています。

    検討用なので採用はしてもらえないですが自分の知識になればこれも良い経験かと思い頑張っていきます。
    ありがとうございました。

    キャンセル

0

固定小数の概念を教えて頂きありがとうございます。
端数処理や扱える値の範囲を明確にしておけば綺麗に表現出来る事を理解しました。
見直してみるとx100000とか確かに煩雑過ぎますね。
これで画像処理を検討しようと思います

/*
  小数を使って2byte(16bit)の変数を整数(8bit)/小数(8bit)で計算する

  シフト演算を使うので処理が速い事や分かり易いのが特徴
  扱える値の範囲に限界があるので事前に決めておく必要がある
  端数処理を意識しないと結果が大きく変わる (1/3)*3 ≠ 1
*/
#define SFT  (8)         /* Q8-format */
#define K   (1<<(SFT-1)) /* K = 0.5 */

int d1 = 87; // 被除数
int d2 = 31; // 除数

// 固定小数の除算
unsigned int ui = (d1 << SFT) / d2;

// 商に除数をかければ被除数になる
ui = ((ui * d2) + K) >> SFT;

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 89.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る