4

Doubleの代わりに任意精度の浮動小数点/ドロップを作成するために、FFIを使用してMPFRをラップしようとしていますが、すべての努力にもかかわらず、最も単純なコードは機能しません。コンパイルして実行しますが、しばらく動作するふりをした後、嘲笑してクラッシュします。単純なCバージョンのコードは、数値「1」から(小数点以下640桁)までの合計10,000回をうまく出力します。Haskellバージョンは、同じことをするように求められた場合、「1.0000 ... 0000」の289回の印刷後、および385回の印刷後、アサーションの失敗と爆弾を引き起こし、データをサイレントに破損(?)します。「動作するはず」なので、これをデバッグする方法がわかりません。

コードはhttp://hpaste.org/10923で閲覧でき、 http: //www.updike.org/mpfr-broken.tar.gzでダウンロードできます。

FreeBSD6ではGHC6.83を、Mac OSXではGHC6.8.2を使用しています。ライブラリとヘッダーファイルの正しいパス(Makefileを変更)とともにMPFR(2.3.2でテスト済み)をインストールする必要があることに注意してください。 GMPから)これを正常にコンパイルします。

質問

  • なぜCバージョンは機能するのに、Haskellバージョンはフレークアウトするのですか?FFIに近づくときに他に何が欠けていますか?StablePtrsを試しましたが、まったく同じ結果になりました。

  • 他の誰かが私のコードをコンパイルして実行することにより、これがMac / BSDのみの問題であるかどうかを確認できますか?(Cコードは「機能」しますか?Haskellコードは「noworks」は機能しますか?)LinuxとWindowsの誰かがコンパイル/実行して、同じ結果が得られるかどうかを確認できますか?

Cコード:(works.c)

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  

#include <gmp.h>  
#include <mpfr.h>
#include "mpfr_ffi.c"  

int main()  
{  
  int i;  
  mpfr_ptr one;  

  mpf_set_default_prec_decimal(640);  

  one = mpf_set_signed_int(1);  
  for (i = 0; i < 10000; i++)
    {  
      printf("%d\n", i);
      mpf_show(one);
    }  
}  

Haskellコード:(Main.hs---動作しません)

module Main where  

import Foreign.Ptr            ( Ptr, FunPtr )  
import Foreign.C.Types        ( CInt, CLong, CULong, CDouble )  
import Foreign.StablePtr      ( StablePtr )  

data MPFR = MPFR  

foreign import ccall "mpf_set_default_prec_decimal"  
    c_set_default_prec_decimal          :: CInt -> IO ()  
setPrecisionDecimal                     :: Integer -> IO ()  
setPrecisionDecimal decimal_digits = do  
    c_set_default_prec_decimal (fromInteger decimal_digits)  

foreign import ccall "mpf_show"  
   c_show                               :: Ptr MPFR -> IO ()  

foreign import ccall "mpf_set_signed_int"  
   c_set_signed_int                     :: CLong -> IO (Ptr MPFR)  

showNums k n = do  
   print n  
   c_show k  

main = do  
   setPrecisionDecimal 640  
   one <- c_set_signed_int (fromInteger 1)  
   mapM_ (showNums one) [1..10000]  
4

3 に答える 3

4

Judah Jacobsenは、Haskell-cafeのメーリングリストでこれに答えました。

これは、 GHCが内部で(整数を維持するために)GMPを使用する方法のため、GHCの既知の問題です。

明らかに、ヒープ内のCデータは、FFIを使用してGMPにアクセスするコードまたはGMPに依存するCライブラリ(私が使用したかったMPFRなど)を除いて、基本的にすべての場合にGHCによってそのまま残されます。いくつかの回避策(苦痛)がありますが、「正しい」方法は、GHCをハックする(ハード)か、サイモンにGHCのGMPへの依存を取り除く(ハード)ことです。

于 2008-10-06T04:13:14.103 に答える
3

私も問題を見て、

$ uname -a
Linux burnup 2.6.26-gentoo-r1 #1 SMP PREEMPT Tue Sep 9 00:05:54 EDT 2008 i686 Intel(R) Pentium(R) 4 CPU 2.80GHz GenuineIntel GNU/Linux
$ gcc --version
gcc (GCC) 4.2.4 (Gentoo 4.2.4 p1.0)
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 6.8.3

また、出力が 1.0000...000 から 1.0000...[ガベージ] に変化することもわかります。

見てみましょう、以下が機能します:

main = do
    setPrecisionDecimal 640
    mapM_ (const $ c_set_signed_int (fromInteger 1) >>= c_show) [1..10000]

oneこれにより、実行時に何らかの形で破壊されるという部分に問題が絞り込まれます。ghc -Cただし、 andの出力を見ghc -Sても、ヒントは得られません。

うーん、./noworks +RTS -H1Gまた動作し、./noworks +RTS -k[n]kのさまざまな値に対して[n]、さまざまな方法で失敗を示しています。

確固たる手がかりはありませんが、2 つの可能性が頭に浮かびます。

  • GHC ランタイムが使用する GMP と、奇妙な相互作用を持つ MPFR
  • GHC ランタイム内で呼び出される C 関数のスタック スペースが制限されており、MPFR が適切に処理されない

そうは言っても... HMPFRを使用するのではなく、独自のバインディングを展開している理由はありますか?

于 2008-10-06T05:07:54.397 に答える
1

HMPFR のメンテナーである Aleš Bizjak は、haskell -cafe に投稿し、GHC が手足の割り当てを制御しないようにする方法を示しました (したがって、手足を GC して破壊するのではなく、手足をそのままにしておきます)。

mpfr_ptr mpf_new_mpfr()  
{  
  mpfr_ptr result = malloc(sizeof(__mpfr_struct));  
  if (result == NULL) return NULL;  
  /// these three lines:  
  mp_limb_t * limb = malloc(mpfr_custom_get_size(mpfr_get_default_prec()));  
  mpfr_custom_init(limb, mpfr_get_default_prec());  
  mpfr_custom_init_set(result, MPFR_NAN_KIND, 0, mpfr_get_default_prec(), limb);  
  return result;  
}

私にとって、これは GHC で GMP の代替を書く努力に参加するよりもはるかに簡単です。これは、GMP に依存するライブラリを本当に使用したい場合の唯一の代替手段です。

于 2008-10-06T18:10:49.530 に答える