unsigned
とfloat
の間の変換は、x86では単純ではありません。 (AVX512まで)それについての単一の指示はありません。一般的な手法は、署名して変換して結果をフィックスアップすることです。これを行うには複数の方法があります。 (this Q&A for some manually-vectorized methods with C intrinsicsを参照)。
MSVCは、最初の128戦略を1つの戦略でベクトル化し、最後のスカラー要素に対して異なる戦略(ベクトル化しない)を使用します。 double
、次にdouble
からfloat
までです。
gccとclangは、ベクター化されたスカラー法の結果から2570980608.0
を生成します。 2570980608 - 2570980487 = 121
と2570980487 - 2570980352 = 135
(入力/出力の丸めなし)、gccとclangはこの場合丸められた結果を返します(0.5ulp未満の誤差)。それが可能なすべてのuint32_tに当てはまるのであればIDK(但し、そのうち2^32しかありません、we could exhaustively check)。ベクトル化されたループのMSVCの最終結果は0.5ulpよりもわずかに誤差がありますが、スカラー法はこの入力に対して正しく丸められています。
IEEE数学は+
-
*
/
とsqrt
が正しく生成(エラー未満0.5ulp)の結果を丸め、他の機能は(等log
)は、このような厳密な要件がないことを要求します。 IDKはint-> float変換のための丸めにどのような要件があるかをIDKがMSVCの場合は厳密に(もしあなたが/fp:fast
などを使わなかったなら)正当なものです。
Bruce DawsonのFloating-Point Determinism blog post(彼の優れたFP数学シリーズの一部)も参照してください。ただし、整数< - > FP変換については言及していません。私たちは、MSVCはをやったOPによってリンクASMで見ることができます
(唯一面白いの指示までストリッピングし、手でコメント):
; Function compile flags: /Ogtp
# assembler macro constants
_arr_dst$ = -1040 ; size = 516
_arr$ = -520 ; size = 516
_main PROC ; COMDAT
00013 mov edx, 129
00018 mov eax, -1723986809 ; this is your unsigned 2570980487
0001d mov ecx, edx
00023 lea edi, DWORD PTR _arr$[esp+1088] ; edi=arr
0002a rep stosd ; memset in chunks of 4B
# arr[0..128] = 2570980487 at this point
0002c xor ecx, ecx ; i = 0
# xmm2 = 0.0 in each element (i.e. all-zero)
# xmm3 = [email protected] (a constant repeated in each of 4 float elements)
####### The vectorized unsigned->float conversion strategy:
[email protected]: ; do{
00030 movups xmm0, XMMWORD PTR _arr$[esp+ecx*4+1088] ; load 4 uint32_t
00038 cvtdq2ps xmm1, xmm0 ; SIGNED int to Single-precision float
0003b movaps xmm0, xmm1
0003e cmpltps xmm0, xmm2 ; xmm0 = (xmm0 < 0.0)
00042 andps xmm0, xmm3 ; mask the magic constant
00045 addps xmm0, xmm1 ; x += (x<0.0) ? magic_constant : 0.0f;
# There's no instruction for converting from unsigned to float, so compilers use inconvenient techniques like this to correct the result of converting as signed.
00048 movups XMMWORD PTR _arr_dst$[esp+ecx*4+1088], xmm0 ; store 4 floats to arr_dst
; and repeat the same thing again, with addresses that are 16B higher (+1104)
; i.e. this loop is unrolled by two
0006a add ecx, 8 ; i+=8 (two vectors of 4 elements)
0006d cmp ecx, 128
00073 jb SHORT [email protected] ; }while(i<128)
#### End of vectorized loop
# and then IDK what MSVC smoking; both these values are known at compile time. Is /Ogtp not full optimization?
# I don't see a branch target that would let execution reach this code
# other than by falling out of the loop that ends with ecx=128
00075 cmp ecx, edx
00077 jae [email protected] ; if(i>=129): always false
0007d sub edx, ecx ; edx = 129-128 = 1
...既知のいくつかは、より多くのばかげ-at-コンパイル時以降のジャンプ...双方向のOで
######## The scalar unsigned->float conversion strategy for the last element
[email protected]:
00140 mov eax, DWORD PTR _arr$[esp+ecx*4+1088]
00147 movd xmm0, eax
# eax = xmm0[0] = arr[128]
0014b cvtdq2pd xmm0, xmm0 ; convert the last element TO DOUBLE
0014f shr eax, 31 ; shift the sign bit to bit 1, so eax = 0 or 1
; then eax indexes a 16B constant, selecting either 0 or 0x41f0... (as whatever double that represents)
00152 addsd xmm0, QWORD PTR [email protected][eax*8]
0015b cvtpd2ps xmm0, xmm0 ; double -> float
0015f movss DWORD PTR _arr_dst$[esp+ecx*4+1088], xmm0 ; and store it
00165 inc ecx ; ++i;
00166 cmp ecx, 129 ; } while(i<129)
0016c jb SHORT [email protected]
# Yes, this is a loop, which always runs exactly once for the last element
f比較、clang、gccもコンパイル時にすべてを最適化するわけではありませんが、それらはクリーンアップループを必要とせず、それぞれのループの後に単一のスカラーストアまたは変換を行うだけです。 (あなたが指示しない限り、実際にはすべてが完全に展開されます)
Godbolt compiler explorerのコードを参照してください。
gccは、上下の16b半分を別々に浮動小数点に変換し、それらを65536で乗算して加算します。
Clangのunsigned
- >float
変換戦略は面白いです:それは決してcvt
命令を使用しません。私は、符号なし整数の2つの16ビット半分を2つの浮動小数点の仮数に直接埋め込むと思う(指数を設定するいくつかのトリックで(ビット単位のブール値とADDPS)、gccのように下半分と上半分を一緒に追加する。
もちろん、64ビットコードにコンパイルする場合、スカラー変換ではuint32_t
を64ビットにゼロ拡張し、署名付きint64_tとしてfloatに変換できます。 signed int64_tはuint32_tのすべての値を表すことができ、x86は64ビット符号付きintを効率的にfloatに変換できます。しかし、それはベクトル化しません。
私の前のコメントは少し愚かでした。アセンブリを生成して送信できますか?どのような結果が得られますか? – Asu
を試すにはBollingers Answer ARR_SIZEをベクトル化幅偶数に減らします。それが結果を変更するかどうかを確認してください。 – Andreas
@Asu VS2015が出力するもの:https://gist.github.com/senyai/3e4b6a9118418d1536476218459cd12d – Senyai