2017-12-04 7 views
2

uint64_tの整数部分を計算したいと思います。 32ビットのuint32_tの場合は、最初にdoublesqrtにキャストしてからuint32_tにキャストすることをお勧めします。整数の精度sqrt doubleを使用

は、2^53までの数字に正確に対応できるとすれば、uint64_tでも機能しますか?すなわち、常に次のように正しい答えを与えるために起こっている:でも

#include <math.h> 
uint64_t x = ...; 
uint64_t result = (uint64_t)sqrt((double)x); 

かを:

#include <math.h> 
uint64_t x = ...; 
uint32_t result = (uint32_t)sqrt((double)x); 
+0

下のコメントで根本的な原因を特定するための@EricPostpischilへ

2.クレジットは、これが唯一の信頼性があります浮動小数点演算では実装が良好です。 C標準だけではこれは必要ありません。数学ライブラリの中には、正確な平方根を表現できる値であっても、おおよその結果しか返さないものがあります。 –

+0

私は32ビット整数の二重戦略を推薦しましたが、それはJavaの質問に応答していました。[回答](https://stackoverflow.com/a/15212684/1798593)答えはJava固有の保証に依存しており、Cには適用されません。 –

答えて

4

経験的に、答えはではありません。 4503599761588224の入力の結果は、67108864ではなく67108865と誤って計算されます。

次のコードは、このケースを識別しています。 もちろん、break;を削除して他のケースを観察することもできます。

#include <stdio.h> 
#include <stdint.h> 
#include <math.h> 

int main(void) { 
    for (uint32_t y = 1; y != 0; y++) { 
     // *Just* smaller than a perfect square 
     uint64_t x = ((uint64_t)y * (uint64_t)y) - 1; 

     // We expect the floor of the result  
     uint32_t expected = y - 1; 

     uint32_t result = (uint32_t)sqrt((double)x); 

     if (result != expected) { 
      printf("Incorrect: x = %llu, result = %u\n", x, result); 
      break; 
     } 
    } 
    return 0; 
} 

値4503599761588224の特長は何ですか?まあ、それはまさに(2 +1) - 1、AKA(2 + 2 )です。これは正確にdoubleで表すことができるので、エラーはlong - >doubleの変換によるものではありません。

代わりに、エラーはsqrt実装の内部にあります。ここでのデルタ(完全な正方形に対する)は、平方根を約2×-27だけ減少させ、それは約2 倍であり、それ自体resultより小さい。これは、倍精度で扱うことができる限界にあります。この時点では、当然、オフ・バイ・ワンのエラーが発生することが予想されます。


1. Live demo。あなたは数学ライブラリの `sqrt`が良いですし、あなたのCを知っている場合でも、` uint32_t`ため:)

+1

もちろん、数学ライブラリが適切であり、正しく丸められた平方根を返したとすると、結果は近似で簡単にテストできます。整数演算で修正してください。 –

+2

2 \ * \ * 26で障害が発生する理由は、sqrt(x)の導関数が1 /(2 \ * sqrt(x))であることです。したがって、(2 \ * \ * 26)** 2では、1を引いたものが平方根を約2 \ * \ * - 27減らします。平方根は2 \ * \ * 26のすぐ下にあるので、平方根の53倍の2倍の減算が行われるので、倍精度の端にちょうど当たったことになります。 –

+0

数学ライブラリが非常に正確な場合でも、平方根を括弧でくくってバイナリ検索を行うことができます。 –

関連する問題