2011-05-16 8 views
6
Result:= b8; 
Result:= Result+ (b7 SHL 8); 
Result:= Result+ (b6 SHL 16); 
Result:= Result+ (b5 SHL 24); 
Result:= Result+ (b4 SHL 32); 
Result:= Result+ (b3 SHL 40);  <------ here 
Result:= Result+ (b2 SHL 48); 
Result:= Result+ (MSB SHL 56); 

(結果はint64、b1-b8はバイトです)int64変数でSHL(左シフト)を使用できません

しかし、コンパイラは "constant exp violations subrange bounds"について不平を言っています。 次に、この警告を見つけました。「コンパイラは、データ型がInt64でない限り、32を超えるハードコードされたシフト右値を拒否します。ウェブサイトのShlには当てはまりません。 Delphiのヘルプには何も言われていません。なぜ限界がありますか?私はそれを停止することはできますか?たぶんコンパイラ指令なのでしょうか?私はそれを乗り越えるために乗算を使うことができますが、遅くなります。それを行うより速い方法がありますか?

編集: 私は枢機卿にSHLを使用することができるので、2つの枢機卿を作成することも考えています。その後、枢機卿を併合します。これには1回の乗算しか必要ありません。他のアイデア?

Edit2: このコードは、数値形式の基数255を基数256に変換するアルゴリズムの一部です(逆も同様です)。私はこれを5億回行う。速度は重要です。

+3

結果はInt64型ですか?そしてb3も? Int64に型キャストを挿入できますか?Int64(b3)? – Andreas

+0

申し訳ありません。ごめんなさい。私はそれを完全に忘れてしまった。私は私の質問を更新しました。 – Ampere

答えて

16

私はすべての算術演算を避ける(あなたのコードは加算とシフトを持っている)、このようにそれを行うだろう:

Int64Rec(Result).Bytes[0] := b8; 
Int64Rec(Result).Bytes[1] := b7; 
//etc. 

Int64Recは次のようにsysutilsの中で定義されています。あなたが保存されている場合

Int64Rec = packed record 
    case Integer of 
    0: (Lo, Hi: Cardinal); 
    1: (Cardinals: array [0..1] of Cardinal); 
    2: (Words: array [0..3] of Word); 
    3: (Bytes: array [0..7] of Byte); 
end; 

あなた配列のバイト数を指定すると、forループ内にすべてをラップすることができます。

+0

downvoteは復讐のハーバードですか? –

+0

私はこれを落としたとは思わないと思います。私はしませんでした。 ;-) –

+0

@TOndrej私のコメントはそれを見つけることを目的としていました。 –

10

私は、あなたのResultはすでにInt64、およびb8であると仮定b7などByteとして宣言されています。コンパイラは少し助けが必要です。 Int64に型キャスト:ここ

Result := b8; 
    Result := Result + (b7 shl 8); 
    Result := Result + (b6 shl 16); 
    Result := Result + (b5 shl 24); 
    Result := Result + (Int64(b4) shl 32); 
    Result := Result + (Int64(b3) shl 40); 
    Result := Result + (Int64(b2) shl 48); 
    Result := Result + (Int64(msb) shl 56); 
+0

'b4 shl 32 = 0' –

+0

間違いがありました。修正しました。 –

+0

私はまだこの解決策に激しくアレルギー反応を起こしています。私はそれが動作することができますが、なぜこの算術、ビットシフト、魔法の定数を使用することに同意する?確かに各バイトを直接割り当てるのが最善でしょうか?私はエレガントなオプションが存在する場合、このようなコードを保証することはできません。 –

7

は、シフトを使用する別の解決策ですが、私は「クリーナー」(デビッドの提案のようにきれいではないが)であると考えている:

result := MSB; 
result := (result shl 8) or b2; { could use "shl sizeof(b2)" instead of 8 } 
result := (result shl 8) or b3; 
etc 
result := (result shl 8) or b8; 

このソリューションは、すべての「魔法」を回避おそらく分かりやすいでしょう。また、MSB..b8(またはb1..b8)がバイトの配列であった場合、上記のコードは簡単に1つのラインループに変換できます。

は、コンパイラが上記の値40とを受け入れていない理由として、あなたの質問に答えるために、理由はSHLD命令のインテル命令セット・リファレンスのボリューム2Bからのこの引用可能性が最も高いです

非64ビットモードではデフォルトの64ビット モードです。カウント0〜4のビットのみが使用されます。このマスク カウントがオペランド サイズよりも大きい場合には0と31の間の値へのカウント は、結果は未定義です。

SHL命令の同等の条件がそれほど厳しくないです

8086は、シフト 数をマスクしません。しかし、(インテル 286プロセッサから始まる)他のすべてのIA-32 プロセッサは、このマスキングは で行われる31の最大 数で、その結果、シフト・カウントを 5ビットをマスク行うすべての動作モード( を含みます仮想8086モード)を使用して、 命令の最大実行時間を 減らしてください。

いずれの場合でも、31を超える値は無用(SHLを使用する場合)または未定義(SHLDを使用する場合)のいずれかです。コンパイラはこれを明らかに知っており、潜在的に誤りのあるコードを書くことを妨げている(64ビットモードで)。

実際にこの操作を50億回実行している場合は、

asm 
    xor eax, eax 
    or eax, MSB { or b1 depending on how you named the MSB } 
    shl eax, 8 
    or eax, b2 
    shl eax, 8 
    or eax, b3 
    shl eax, 8 
    or eax, b4 
    mov High32bit, eax 
end; 

と下位32ビットのダブルワード及びB8スルーB5のための上記動作を繰り返す:非常に簡単になり、インラインアセンブリ。 Delphiが64ビットレジスタや64ビット命令をサポートしているとは思えないので、私はSHLDを使用することを提案しませんでした(私は間違っているかもしれませんが、試したことはありません)。

注: Delphiのバージョンはインラインアセンブリをサポートしません。これは実際の場合とそうでない場合がありますが、本当に必要な場合を除き、インラインアセンブリから離れています。

PS:David Heffernanの解決策が最高の理由があります。私が提示したソリューションでは、すべての命令は前の命令に依存しています(つまり、次の "or"命令を実行する前にeaxを8だけシフトしなければなりません)。 Davidのソリューションは個々のバイトを設定するので、各割り当ては以前の割り当てとは独立しています。これにより、プロセッサは複数の割り当てを並列で実行する可能性があります。マルチコアプロセッサのこの時代では、これは例として与えたアセンブリコードよりもかなり速いという可能性を秘めています。

+3

+1が最も優れた答えです –

関連する問題