それだけです、ポむント、出航したした 浮動小数点数を操䜜し、小数の固定粟床で代替を開発する方法を孊びたす。





今日は実数に぀いおお話したす。 より正確には、分数数量の蚈算におけるプロセッサによる衚瀺に぀いお。 私たちはそれぞれ、3.5の代わりに3,4999990123ずいう圢匏の数倀の文字列を出力するこずに盎面したした。さらに悪いこずに、理論的な結果ずプログラムコヌドの実行の結果ずしお行われた結果ずの蚈算埌の倧きな違いです。 これにはひどい秘密はありたせん。浮動小数点衚珟アプロヌチの長所ず短所に぀いお説明し、代替の固定小数点パスを怜蚎し、固定粟床で小数のクラスを蚘述したす。



ポむントはどこに行きたすか



プロセッサが必ずしも実数を理解しおいなかったのは秘密ではありたせん。 プログラミング時代のd明期、最初のコプロセッサヌが登堎する前は、実数はハヌドりェアレベルでサポヌトされおおらず、プロセッサヌがうたく機胜する敎数を䜿甚しおアルゎリズム的に゚ミュレヌトされおいたした。 したがっお、叀き良きパスカルの実数型は珟圚の実数の先祖でしたが、ビットが実数の仮数ず指数ずしお論理的に解釈される敎数の䞊䜍構造でした。



仮数は、本質的にドットなしで曞かれた数字です。 指数は、特定の数N通垞、N = 2を䞊げる必芁がある床合いです。そのため、仮数を掛けるず、目的の数が埗られたす仮数の桁容量に正確。 次のようになりたす。



x = m * N ^ e,  m  e —  ,         .
      
      





あいたいさを避けるため、1 <= | m | <N、぀たり、番号はカンマの前に1぀の蚘号が付いおいるように曞き蟌たれたすが、コンマは悪意を持っお削陀され、番号は敎数になりたした。



仮数は、本質的にドットなしで曞かれた数字です。 指数は、特定の数N通垞N = 2を䞊げる必芁がある床合いです。仮数を掛けるず、目的の数が埗られたす。


最近では、実数を持぀すべおのアルゎリズムず同様に、開発者は浮動小数点挔算を避けようずしたした。 実数を䜿甚したコプロセッサヌ凊理操䜜は、すべおのプロセッサヌで実行されたわけではありたせんが、実際には、垞に効率的に機胜するずは限りたせんでした。 しかし、時間が経ち、珟圚では浮動小数点挔算がプロセッサコアに組み蟌たれおいたす。さらに、ビデオチップは実数も積極的に凊理し、同じタむプの挔算を䞊列化したす。


プロセッサレベルでハヌドりェアによっおサポヌトされる最新の実数も、仮数ず指数に分けられたす。 もちろん、敎数挔算になじみのあるすべおの挔算は、実数のプロセッサ呜什でもサポヌトされおおり、可胜な限り迅速に実行されたす。



そのような蚘録圢匏は、たず、そのような数での乗算および陀算挔算が非垞に効果的であるこずに加えお、そのような圢匏で衚される元の実数を埗るこずも難しくないため、すべおが非垞に耇雑です。 この数倀衚珟は、浮動小数点数ず呌ばれたす。



ダむビング暙準



プロセッサレベルでサポヌトされる浮動小数点実数は、特別な囜際暙準IEEE 754で蚘述されおいたす。 蚈算の䞻な2぀のタむプは、 単粟床 単粟床および倍粟床 倍粟床 浮動小数点  浮動小数点数です。 これらの名前は、単粟床ず倍粟床の数倀のバむナリ衚珟のビット深床を盎接反映したす。単粟床の衚珟には32ビットが割り圓おられ、奇劙なこずに、倍粟床衚珟には64ビットが2回割り圓おられたす。



IEEE 754-2008芏栌の新版は、単粟床ず倍粟床に加えお、4倍粟床、さらには半粟床の拡匵粟床も提䟛したす。 ただし、C / C ++では、floatずdoubleの他に、おそらくlong double型があり、Microsoftでは頑固にサポヌトされおおらず、Visual C ++では代わりに通垞のdoubleを眮き換えたす。 珟時点のプロセッサコアも、原則ずしお、半粟床および4倍粟床のタむプをただサポヌトしおいたせん。 したがっお、浮動小数点ビュヌを遞択する堎合は、floatずdoubleからのみ遞択する必芁がありたす。


暙準による数倀の指数の基瀎ずしお、それぞれ2が䜿甚され、䞊蚘の匏は次のようになりたす。



 x = m * (2 ^ e),  1 <= |m| < 2; m, e — 
      
      





単粟床数のビット単䜍のレむアりトは次のようになりたす。



| 蚘号の䞋の1ビット| 8ビット指数| 23ビットの仮数|



倍粟床の堎合、より倚くのビットを䜿甚できたす。



| 蚘号の䞋の1ビット| 11ビット出展者| 52ビットの仮数|



どちらの堎合も、笊号ビットが0の堎合、数倀は正であり、負の数倀には1が蚭定されたす。 この芏則は敎数に䌌おいたすが、唯䞀の違いは、敎数ずは察照的に、加算の反察の数倀を取埗するには、笊号の1ビットを反転するだけで十分であるずいうこずです。



仮数はバむナリで曞き蟌たれるため、敎数郚分はすでに1に等しいため、仮数レコヌドでは垞にバむナリレコヌドに栌玍されおいない1ビットを意味したす。 仮数のビットは、正芏化された数倀の小数郚分を2進衚蚘で正確に栌玍したす。



仮数はバむナリ圢匏で蚘述され、敎数郚分明らかに1に等しいは砎棄されるため、仮数がバむナリ圢匏で栌玍されるよりも1ビット長いこずを決しお忘れたせん


この暙準で衚すこずができる数倀の小数点以䞋の粟床を蚈算するために博士号を取埗する必芁はありたせん。2^23 + 1= 16 777 216; これは、単粟床で実数を衚珟する粟床が小数点以䞋7桁をわずかに超えるずいう事実を明らかに瀺しおいたす。 これは、この圢匏では保存できないこずを意味したす。たずえば、123,456.78ずいう数字は䞀般に小さい数字ですが、100分の1から始たる数字は間違っおいたす。 32ビット敎数にも完党に適合する1 234 567 890ずいう圢匏の倚数の堎合、数癟単䜍で゚ラヌが発生するため、状況は耇雑です。 したがっお、たずえば、C ++では、実数の堎合、デフォルトでdouble型が䜿甚されたす。 倍粟床の数倀の仮数は、すでに15桁を超えおいたす2 ^ 52> = 4 503 599 627 370 496すべおの32ビット敎数に静かに察応し、本圓に倧きな64ビット敎数10進数19桁でのみ゚ラヌが発生したす。原則ずしお、数癟単䜍ではすでに重芁ではありたせん。 より正確な情報が必芁な堎合は、この蚘事で必ず圹立ちたす。



出展者の方ぞ。 これは通垞の敎数のバむナリ衚珟で、10を环乗しお正芏化圢匏の仮数を乗算しお元の数を取埗する必芁がありたす。 さらに、暙準ではオフセットが導入されおいるだけであり、10のべき乗 いわゆるバむアス指数 -オフセット指数を埗るために、バむナリ衚珟から枛算する必芁がありたす。 比范挔算を簡玠化するために指数がシフトされたす。぀たり、単粟床の堎合は倀127が、倍粟床の堎合1023が取埗されたす。これらはすべお非垞に耇雑に聞こえるので、浮動小数点型の章をスキップしたす。 しかし、無駄に



おおよその氎泳



少し明確にするために、䟋を考えおみたしょう。 数倀640 = 512 + 128を単粟床の実数ずしおバむナリ圢匏で゚ンコヌドしたす。





倍粟床の堎合、ほずんどすべおが同じになりたすが、仮数郚の小数郚の右偎にはさらに倚くのれロが含たれ、指数は1023 + 9 = 1024 + 8になりたす。぀たり、最䞊䜍ビットず指数の数の間のれロより少し倧きいです 、慎重に理解すれば、すべおがそれほど怖いわけではありたせん。



宿題次の定数のバむナリ衚蚘を理解するプラスずマむナスの無限倧 INF-無限倧、れロ、マむナスれロ、および数ではない数 NaN-数ではない。



ブむのために泳ぐな



1぀の重芁なルヌルがありたす。数倀を衚珟するための各圢匏には、泳ぐこずができない独自の制限がありたす。 さらに、C / C ++プログラムの動䜜は、2぀の倧きな正の敎数の远加ずしお小さな負が䞎えられたずきに穏やかな顔をするこずであるため、これらの制限を超えないようにするこずはプログラマ次第です。 ただし、敎数の堎合、最倧倀ず最小倀のみを考慮する必芁がある堎合、浮動小数点衚珟の実数に぀いおは、数倀のビット深床よりも最倧倀に泚意を払う必芁がありたす。 指数のため、倍粟床の浮動小数点衚珟の最倧数は10 308を超えたす。単粟床の指数でも10 38を超える数倀を゚ンコヌドできたす。 64ビット敎数の最倧倀である「悲惚な」10 19ず比范するず、最倧倀ず最小倀を考慮する必芁はほずんどないず結論付けるこずができたすが、それらを忘れおはなりたせん。



敎数に぀いお、最倧倀ず最小倀のみを考慮する必芁がある堎合、浮動小数点衚珟の実数に぀いおは、数倀のビット深床に関しおは最倧倀よりも泚意を払う必芁がありたす。


もう1぀は、粟床の問題です。 仮数の䞋の悲惚な23ビットは、小数点第8䜍で既に゚ラヌを発生させたす。 倍粟床の数倀の堎合、状況はそれほど嘆かわしいものではありたせんが、たずえば、デヌタ凊理でポむントの埌に6぀の固定文字が必芁で、ポむントたでの数倀が非垞に倧きく、その䞋に残っおいるのが9文字だけである堎合、小数点以䞋15桁が非垞にすぐに問題になりたす。 したがっお、数十億ドルの金額があるず、小数郚分に倧きな誀差が生じたす。 このような数の凊理を集䞭的に行うず、数十億ナヌロが「収たらなかった」ずいう理由だけで消滅する可胜性があり、小数郚分の誀差が合蚈され、膚倧な残高の未蚈䞊デヌタが蓄積されたした。



それだけが理論だったら 実際には、1000分の1セントも消えおはならず、すべおの操䜜の゚ラヌは厳密にれロに等しくなければなりたせん。 したがっお、ビゞネスロゞックでは、原則ずしお、C / C ++を䜿甚したせんが、CたたはPythonを䜿甚したす。ここで、Decimal型は既に暙準ラむブラリで構築されおおり、指定された小数点以䞋桁数の粟床で゚ラヌなしの小数郚を凊理したす。 高床なプログラミング蚀語を䜿甚せずに非垞に倧きな数を凊理するタスクに盎面した堎合、C ++プログラマは䜕をすべきでしょうか はい、い぀もず同じです。10進型の高レベルラむブラリず同様に、高粟床の小数郚を操䜜するための1぀の小さなデヌタ型を䜜成しおギャップを埋めたす。



浮動小数点セメントを远加する



浮動小数点を修正したす。 蚈算の粟床に問題があるため、浮動小数点型を削陀するこずに決めたため、敎数型が残っおいたす。最倧ビット深床が必芁なため、最倧ビット幅が64ビットの敎数型が必芁です。



今日、教育目的のために、期間埌の18文字たでの粟床を保蚌した実数の衚珟を䜜成する方法を怜蚎したす。 これは、敎数郚分ず小数郚分にそれぞれ2぀の64ビット敎数を単玔に組み合わせるこずによっお実珟されたす。 原則ずしお、各コンポヌネントの単䞀の番号の代わりに、倀の配列を取埗しお完党な「長い」算術を取埗するこずを誰も気にしたせん。 しかし、粟床の問題を解決するには十分であるため、小数点の前埌に18桁の粟床で䜜業し、これら2぀の倀の間のポむントを固定し、セメントで埋めるこずができたす。



私にも小数を泚ぎたす



たず、少しの理論。 敎数ず敎数の小数郚分の2぀のコンポヌネントをnずfずしお瀺したす。数倀自䜓は次の圢匏で衚珟できたす。



x = n + f * 10 -18 n、fは敎数、0 <= f <10 18



敎数郚分に぀いおは、64ビット敎数の笊号付きタむプが最適であり、小数郚分笊号なしの堎合、これにより将来の倚くの操䜜が簡玠化されたす。



 class decimal { 
 private: int64_t m_integral; uint64_t m_fractional; };
      
      





この堎合の敎数郚は、衚珟された数倀よりも小さい最倧敎数です。小数郚は、この数倀から敎数郚を10 18倍しお敎数に枛じた結果ですf =x-n* 10 18 。



負の数倀の敎数郚分は、数倀自䜓を法ずしお倧きくなり、小数郚分は数倀自䜓の10進衚蚘ず䞀臎したせん。たずえば、数倀–1.67の堎合、コンポヌネントはn = –2およびf = 0.33 * 10 18になりたす。 しかし、このようなレコヌドを䜿甚するず、負の数に分岐が必芁ないため、加算および乗算アルゎリズムを単玔化および高速化できたす。



10進型の操䜜



もちろん、粟床が向䞊した数倀のタむプは、算術挔算なしでは圹に立ちたせん。 远加は比范的簡単です



 x = a + b * 1e-18, y = c + d * 1e-18, z = x + y = e + f * 1e-18, a, c, e: int64_t; b, d ,f: uint64_t; 0 <= b, d, f < 1e+18, z = (a + b * 1e-18) + (c + d * 1e-18) e = a + c + [b * 1e-18 + d * 1e-18] f = {b * 1e-18 + d * 1e-18} * 1e+18
      
      





NB以降、1eの圢匏のすべおの゚ントリは敎数です。



ここで、[n]は数倀の敎数郚分を取埗し、{n}は小数郚分を取埗しおいたす。 すべお問題ありたせんが、敎数の制限を芚えおおいおください。 倀1e + 18は、笊号なし64ビット敎数型uint64_tの倀の限界に既に近づいおいたすそのため、遞択したした。



 e = a + c + (b + d) div 1e+18, f = (b + d) mod 1e+18.
      
      





数倀を䜿甚した操䜜を実装するずきは、集䞭的な䜿甚を䌎うため、垞に2぀のこずを考慮する必芁がありたす。最初に、乗算ず陀算の操䜜を最小限に抑えるために垞にアルゎリズムを最適化する必芁がありたす。そのため、最初のポむントが簡単に実行されるように、数匏を事前に単玔化する䟡倀がありたす この堎合、すべおを敎数の陀算に最小化し、残りを必芁ずしたす。 第二に、蚈算された型の境界を越えお数倀がオヌバヌフロヌする可胜性のある状況をすべお確認する必芁がありたす。そうしないず、型を䜿甚するずきに非垞に明癜な゚ラヌが発生したす。


もちろん、aずcを远加するずきに境界倀をチェックする䟡倀がありたす。 たた、bずdが1e + 18未満であるずいう事実に基づいお、b + d<2 * 1e + 18であるこずがわかりたす。これは、最倧倀が最埌の加算ずしお远加されるこずを意味したす。したがっお、アルゎリズム䞊、陀算をたったく考慮しお実行速床を最適化するこずはできたせん操䜜



 e = a + c; f = b + d; if (f >= 1e+18) f -= 1e+18, ++e;
      
      





ここでは、eの倀の最倧敎数のチェックは、衚瀺を簡単にするために省略されおいたす。



枛算に぀いおは、すべおがもう少し耇雑です。ここでは、笊号なし敎数のれロ未満の遷移を考慮する必芁がありたす。 ぀たり、枛算する前に2぀の数倀を比范する必芁がありたす。



 e = a - c; if (b >= d) f = b - d; else f = (1e+18 - d) + b, --e;
      
      





䞀般的に、これたでのずころすべおが簡単です。 乗算ず陀算の前は、すべおが垞に単玔です。



固定粟床の乗算



最初に乗算を怜蚎しおください。 小数郚分の数倀は非垞に倧きく、原則ずしお1e + 18のすぐ近くにあるため、アセンブラヌ蚀語ずQレゞスタでの操䜜を䜿甚するか、敎数倀をバむパスしお、各コンポヌネントを1e + 9の2぀の郚分に分割する必芁がありたす。 この堎合、乗算は1e + 18以䞋であり、アセンブラはただ必芁ありたせん。それほど高速ではありたせんが、十分な64ビット敎数があり、C ++内に残りたす。 孊校ず列の乗算を芚えおいたすか そうでない堎合は、次のこずに泚意しおください。



a = s a * a 1 -a 2 * 10 -9 ; b = b 1 -b 2 * 10 -9 ;

c = s c * c 1 -c 2 * 10 -9 ; d = d 1 -d 2 * 10 -9 ;

0 <= a 2 、b 2 、c 1,2 、c 1,2 <10 9 ;

s a、c =蚘号a、蚘号c

0 <= a 1 、s 1 <MAX_INT64 / 10 9 9



乗算の蚈算を簡玠化するためのマトリックスを導入したす。



U =a 1 、a 2 、b 1 、b 2 、

V =c 1 、c 2 、d 1 、d 2  T 、

A = V * U、

A =

| a 1 * c 1 a 1 * c 1 b 1 * c 1 b 2 * c 1 |

| a 1 * c 2 a 1 * c 2 b 1 * c 2 b 2 * c 2 |

| a 1 * d 1 a 1 * d 1 b 1 * d 1 b 2 * d 1 |

| a 1 * d 2 a 1 * d 2 b 1 * d 2 b 2 * d 2 |



マトリックスは、蚈算を容易にするためではなく、怜蚌を容易にするために導入されおいたす。 実際、A 11 = a 1 * c 1は厳密にMAX_INT64 / 10 18未満でなければならず、察角線の倀は次のずおりです。A12 = a 1 * c 2およびA 21 = a 2 * c 1は厳密にMAX_INT64 / 109未満でなければなりたせん。コンポヌネントを远加するずきにこれらの係数を乗算したす。



e = A 11 * 10 18 +A 12 + A 21 * 10 9 +A 13 + A 22 + A 31 +A 14 + A 23 + A 32 + A 41 div 10 18 、

f =A 14 + A 23 + A 32 + A 41 mod 10 18 +A 24 + A 33 + A 42 +A 34 + A 43 div 10 9



ここでは、れロに等しいずいう理由だけで、甚語A 44 div 10 18を省略したす。 もちろん、各远加の前に、MAX_INT64を超えおいるかどうかを確認する䟡倀がありたす。 幞いなこずに、行列のすべおのコンポヌネントず䞭間結果に察しお、笊号なしの型uint64_tを操䜜できたす。 最埌に行う必芁があるのは、結果の笊号s e = s a xor s cを決定し、負の数の敎数ず小数郚を修正するこずです。敎数を1枛らし、小数を1から枛算したす。 ここで、䞀般的に、すべおの乗算では、䞻なこずは非垞に泚意するこずです。 アセンブラを䜿甚するず、すべおがはるかに簡単になりたすが、この資料はC ++ Academyの範囲を超えおいたす。



登録およびSMSを䜿甚しない陀算アルゎリズム



列分割アルゎリズムを芚えおいれば、よくできおいたすが、ここでは必芁ありたせん。 数孊ず䞍等匏を持぀小さな魔術のおかげで、逆数x -1を蚈算し、x -1で乗算を実行するのが簡単になりたす。 そこで、問題を解決したす



y = x -1 = 1 /a + b * 10 -18 = c + d * 10 -18 。



簡単にするために、正のxの逆数を芋぀けるこずを怜蚎しおください。 xのコンポヌネントの少なくずも1぀がれロに等しいただし、䞀床に䞡方ではない堎合、蚈算は倧幅に簡玠化されたす。 a = 0の堎合



 y = 1 / (b * 1e-18) = 1e+18 / b, e = 1e+18 div b, f = 1e+18 mod b;  b = 0, a = 1,  y = e = 1, f = 0; ec b = 0, a > 1, : y = 1 / a, e = 0, f = 1e+18 div a.
      
      





より䞀般的な堎合、xに非れロの小数郚ず敎数郚が含たれる堎合、この堎合、方皋匏は次のようになりたす。



  a > 1, b != 0 : y = 1 / (a + b * 1e-18) < 1,  e = 0, f = 1e+18 / (a + b * 1e-18).
      
      





ここで、最倧次数10を芋぀ける必芁がありたす。これはa以䞋であり、次のアクションを繰り返し実行したす。



 k = max(k): 1e+k <= a, u = 1e+18, v = (a * 1e+18-k + b div 1e+k); f = (u / v) * 1e+18-k, for (++k; k <=18; ++k) { u = (u % v) * 10; if (!u) break; //    f += u / v * 1e+(18-k); }
      
      





ここでは、同じ係数10の环乗による分数の乗算ず陀算を䜿甚し、次の10の环乗に察する陀算の陀算ず剰䜙を段階的に蚈算したす。



それらを蚈算するこずは完党に䞍芁であり、事前に知っおおり、しばしば必芁になるため、0から18たでの数十床の配列を持぀こずは非垞に䟿利です。



型倉換



がんやりずした浮動小数点数を2倍にしお、真新しい10進数に倉換するのに十分なこずがわかっおいたす。



 decimal::decimal(double value) : m_integral(static_cast<int64_t>(std::floor(value)) m_fractional(static_cast<int64_t>(std::floor( (value - m_integral) * 1e+18 + 0.5)) { normalize(); } void decimal::normalize() { uint64_t tail = m_fractional % 1e+3; if (tail) { if (tail > 103/2) m_fractional += 1e+3 - tail; else m_fractional -= tail; } }
      
      





ここで、103は、実際には、それを超えるずdoubleが正確でなくなる゚ラヌです。 必芁に応じお、゚ラヌをさらに枛らすこずができたす。ここでは 、衚瀺を明確にするために10 18〜15が必芁です。 ずにかく、倉換埌の正芏化が必芁になりたす。正確にdoubleは10進数の小数郚よりも明らかに䜎いからです。 さらに、doubleがint64_tを超える堎合、そのような条件䞋で、小数郚が数倀の敎数郚を正しく倉換できない堎合を考慮する必芁がありたす。



フロヌトの堎合、すべおが䌌おいたすが、゚ラヌは桁違いに倧きくなりたす10 18-7 = 10 11 。



decimal , m_integral. m_integral, m_fractional.



decimal double float :



 return m_integral + m_fractional * 1e-18;
      
      





. , , , decimal separator , . «» m_precision .



, . , , , , — , .



, decimal, .



GITHUB



decimal, .


, !



, C/C++ . , Python C#, 15–18 , .



decimal , int64_t. , double float , . , decimal . , .



, . double float, , . , , , , . , !



画像



#192.

: Qualab , ++ Parallels




ハッカヌを賌読する




All Articles