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

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

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

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

Q&A

解決済

5回答

12756閲覧

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

kuma_dansyaku

総合スコア18

C

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

0グッド

0クリップ

投稿2017/05/18 07:23

編集2017/05/18 07:25

はじめに

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

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

C

1#include <stdio.h> 2 3#define MILLION_TIMES (1000000) 4 5int main(char argc, char *argv[]) { 6 7 int dividend; // 被除数 8 int divisor; // 除数(0は絶対に使わない) 9 10 dividend = 87; 11 divisor = 31; 12 13 // 確認用です(実数型は使わない) 14 double dDiv = (double)dividend / divisor; 15 16 17 int integer = dividend / divisor; 18 int idec = ((dividend * MILLION_TIMES) / divisor)-(integer * MILLION_TIMES) ; 19 20 21 // 商に除数をかければ被除数になる 22 printf("[double]\n"); 23 printf("%d / %d =%lf\n", dividend, divisor, dDiv); 24 printf("%f * %d =%lf\n", dDiv, divisor, dDiv*divisor); 25 printf("\n"); 26 printf("[int]\n"); 27 printf("%d / %d =%d.%06d\n", dividend, divisor, integer, idec); 28 printf("%d.%d * %d =%d\n", integer, idec, divisor, divisor*integer+(divisor*idec)/ MILLION_TIMES); 29 30 return 0; 31}

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

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

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

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

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

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

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

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

Zuishin

2017/05/18 07:32

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

2017/05/18 08:26

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

2017/05/18 08:35

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

2017/05/18 13:45

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

回答5

0

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

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

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

投稿2017/05/18 08:04

maisumakun

総合スコア145183

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

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

kuma_dansyaku

2017/05/18 14:12

画像縮小です。 1byte/1pixの画像(色の格納方法は話がややこしくなるので考え無いでください) 縮小サイズからベース画像の位置を割り出しそこから上下左右の色情報を取って中間色を得ると言う事をしたいのです。 実際やるなら潤沢なAPI(メソッド)を持った言語でやるか、専用のライブラリを用意する必要があるのですが、何も無いこの状況で実装してくれと言われたので右往左往しています。 > ネイティブで浮動小数点演算のできない環境向けにコードを書く →マイコンに焼くのでその通りです > 精度は犠牲にしていいから、できるだけ高速に計算したい →精度の方が重要です 取りあえず小数の計算が出来れば良いと思っていたのと、仕事の話に密になるので多く書けなかったので、情報が少なすぎると思われましたらすみませんでした。
guest

0

ベストアンサー

こんにちは。

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

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

divisorinteger+(divisoridec)/ MILLION_TIMES

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

(divisorinteger+(divisoridec) + MILLION_TIMES/2)/ MILLION_TIMES

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


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

投稿2017/05/18 09:01

編集2017/05/18 09:14
Chironian

総合スコア23272

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

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

kuma_dansyaku

2017/05/18 14:03

レスありがとうございます。 具体的な手法まで書いて頂き参考になります。 あの後聞いたところunsinged(符号なし) との事なので負数を使う必要は無いとの事なので検討範囲は狭まりました。 上記のコメントにも記載いたしましたが、ソフト処理で画像の縮小を行ってほしいと言われバイニリア、(出来れば)バイキュービックも試して欲しいと言われ「冗談でしょ?なんちゃってバイニリアしか出来ませんよ?」と思いながら色々試していました。 ご提示頂いた2進数で扱う方法(考え方)は初めて知りました。 この辺は皆様から頂いた用語を元に調べてみようとおもいます。
sharow

2017/05/18 21:02

コメント欄で失礼。私も固定小数点が良いと思います。 初代プレステが固定小数点だったことを思いながら取り組むと捗るのではないでしょうか。 固定小数点では浮動小数点よりもサイズ、拡縮度合いが厳密に制限されるので注意してください。 仕様として例えば「1/64より小さい縮小はできない」とか、「2048px以上の画像は扱えない」といった制限をあらかじめ設ける必要があります。
guest

0

wikipedia

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

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

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

投稿2017/05/18 09:47

nullbot

総合スコア910

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

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

kuma_dansyaku

2017/05/18 13:51

レスありがとうございます。 リンク先参考にさせていただきます。 今時の組込みチップなのでFPUはあるはずなのですがそんな無理難題を言われました。 浮動小数点演算ライブラリと言うのがあるんですね。 こっちで使っているコンパイラはなんだったかな?ARMかなんかだったと思いますが、下っ端のペーペーには仕様を流してくれないので言われた情報だけでやっています。 検討用なので採用はしてもらえないですが自分の知識になればこれも良い経験かと思い頑張っていきます。 ありがとうございました。
guest

0

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

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

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

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

投稿2017/05/18 09:03

PineMatsu

総合スコア3579

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

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

kuma_dansyaku

2017/05/18 13:56

レスありがとうございます。 >PC上で動かすソフトの場合は、こういうことはまず考えませんね。 そうなんですよ。言われた時目が点になりました。 画像の縮小処理でバイリニア手法を使うに当たってこの難題にぶち当たりました。 ここまで行くとコンバートになるのでこう言うのはハードの方でやってくれれば良いのに・・・・。 そんな事を思いながらクイズ感覚で色々試しています。 ここまでやって検討用のお試し処理なので実採用はしてもらえないですが自分の知識になればこれも良い経験かと思い頑張っていきます。 ありがとうございました。
guest

0

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

C

1/* 2 小数を使って2byte(16bit)の変数を整数(8bit)/小数(8bit)で計算する 3 4 シフト演算を使うので処理が速い事や分かり易いのが特徴 5 扱える値の範囲に限界があるので事前に決めておく必要がある 6 端数処理を意識しないと結果が大きく変わる (1/3)*3 ≠ 1 7*/ 8#define SFT (8) /* Q8-format */ 9#define K (1<<(SFT-1)) /* K = 0.5 */ 10 11int d1 = 87; // 被除数 12int d2 = 31; // 除数 13 14// 固定小数の除算 15unsigned int ui = (d1 << SFT) / d2; 16 17// 商に除数をかければ被除数になる 18ui = ((ui * d2) + K) >> SFT;

投稿2017/05/23 04:37

編集2017/05/23 14:44
kuma_dansyaku

総合スコア18

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問