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

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

ただいまの
回答率

87.49%

pythonで32bitの浮動小数点の計算をしたい(Cのfloatでの計算結果と同じにしたい)

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 4,581

score 18

C原語===========================
float A = 839.896912;
float B = 0.25000;
float E = 1.000000;
int c = 2480;
int d = 2474;
float g = (A + c * B) / E;
float f = (A + d * B) / E;  
printf("g is %f\n", g);
printf("f is %f\n", f);

g is 1459.896973
f is 1458.396973

上記のC原語と同じ計算をしたくpythonのソースを書きましたが計算結果が合いません

python================
import numpy as np
A = 839.896912
B = 0.25000
E = 1.000000
c = 2480
d = 2474 
g = (A + c * B) / E
f = (A + d * B) / E 
print(g)
print(f)

g is  1459.8969120000002
f is  1458.3969120000002

double型で計算しているため、違いがあることまでわかりましたが
その後の解決方法がわかりません。

やってみたこと(numpyのfloat32)
g = (np.float32(A)  + c * np.float32(B)) / np.float32(E) 
f = (np.float32(A) + d * np.float32(B)) / np.float32(E) 
print("g is ",g)
print("f is ",f)

g is  1459.89691162
f is  1458.39691162

計算結果をC原語と同じにするにはどうしたら良いのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • maisumakun

    2019/01/21 11:00

    「何のために」精度を落としたfloatとして計算したいのでしょうか。

    キャンセル

  • marymills

    2019/01/21 11:31

    もともとはCでバイナリファイルを4バイトづつ読み込み(上記Aは0x4451f967)をfloatで計算してその結果を出しているので、同じように計算し値を出したいということになります。

    キャンセル

回答 2

+3

以下のようにすれば、Cのprint結果と一致はします。
ただし、floatの10進有効桁はたかだか6,7桁しかないので、小数以下6桁出しても下位は意味のない値となります。

import numpy as np

A = 839.896912
B = 0.25000
E = 1.000000
c = 2480
d = 2474

g = (np.float32(A)  + c * np.float32(B)) / np.float32(E)
f = (np.float32(A) + d * np.float32(B)) / np.float32(E)

g = np.float32(g)
f = np.float32(f)
print('{:.6f}'.format(g)) # 1459.896973
print('{:.6f}'.format(f)) # 1458.396973

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/21 15:55

    この方法でCのprint結果と同じになりました。ありがとうごいます。

    キャンセル

checkベストアンサー

+2

結果が異なる点について

C言語ではintとfloatの乗算では両方の値がfloatへ変換された上で演算されますが、numpy.float32はintと乗算すると結果がfloat64(多くのCの実装におけるdouble型)になるようです。(Pythonの整数型(int)が多倍長精度であることに関係するのかも知れません。)

まず計算結果そのものを合わせるにはCと同じ規則で(floatの精度で)演算結果が求まるように注意深く式を記述しなければならないと思います。

from numpy import float32

A = 839.896912
B = 0.25
E = 1.0
c = 2480
d = 2474

## NG

g = (float32(A) + c * float32(B)) / float32(E)
print(type(g), g)  # ==> <class 'numpy.float64'> 1459.8969116210938

## OK

g = (float32(A) + float32(c) * float32(B)) / float32(E)
print(type(g), g)  # ==> <class 'numpy.float32'> 1459.897

余談?: 値が同じでも文字列化(フォーマット)の仕様により違う値に見える

例え同一の値が得られたとしても、C言語でのprintfの結果に一致させるのはPythonでは容易ではないと思います。浮動小数点数を印字する際の文字列への変換仕様が異なるからです。printfの仕様(%fとすると小数点以下6位まで印字されるとか)を前提としてcan110さん回答のように小数点以下の桁数をformatに指定するのが簡単な方法だと思います。しかし%gや%eなど任意の数値・フォーマットについてCのprintfと同じ結果を表示するのはなかなか難しいのではないでしょうか?

ということで、かなりヘンテコな方法かも知れませんが「Cのsprintfをpythonから呼び出してしまうのはどうだろう」と思いやってみました。ctypesモジュールは使ったことないのでおかしな実装をしているかも知れません。なおスミマセンガ下記はWindows限定のコードです。Cのランタイムmsvcrtを使うコードを明示的に書いてしまってます。

from ctypes import *
libc = cdll.msvcrt

def format_double(fmt, f):
    charset = 'latin1'
    _buffer = create_string_buffer(100)
    _fmt = bytes(fmt, encoding=charset)
    libc.sprintf(_buffer, _fmt, c_double(f))
    return str(_buffer.value, encoding=charset)


A = float32(839.896912)
B = float32(0.25)
E = float32(1.0)
c = 2480
d = 2474

g = float32((A + float32(c) * B) / E)
f = float32((A + float32(d) * B) / E)
print('--python--')
print('g is', g)
print('f is', f)
print('--sprintf--')
print(format_double("g is %f", g))
print(format_double("f is %f", f))
> python test.py
--python--
g is 1459.897
f is 1458.397
--sprintf--
g is 1459.896973
f is 1458.396973


(Windows10 64bit, CPython 3.7)

sprintfにdoubleへ変換したものを渡してますがprintfなどにfloatを渡しても実際にはdoubleへ変換されたものが引数となるためこうしています。(自分の知識はCの規格書を正確に把握しているレベルではありません。msvcrtでは%fでfloatもdoubleも印字できることから前述のようになっているはずと判断しているにすぎません。)

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/21 15:52

    詳しく説明ありがとうございます。

    キャンセル

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

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

関連した質問

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