Мне нужно передать некоторые данные из Go в шейдер 300 es. Данные состоят из двух uint16, упакованных в uint32. Каждый uint16 представляет собой число с плавающей запятой половинной точности (float16). Я нашел некоторый код Java PD, который выглядит так, как будто он справится с этой задачей, но я борюсь с переносом последнего оператора, который использует пару сдвигов вправо с нулевым расширением (я думаю, что другие сдвиги в порядке, т. Е. Неотрицательные). Поскольку Go немного умен с расширением, решение для порта ускользает от меня. Я действительно подумал, может быть, первый можно было бы изменить на левый сдвиг, поскольку он просто позиционирует один бит для добавления? но последняя смена сносит мой мозг до глубины души :)
Между прочим, я надеюсь, что правильно сделал скобки, поскольку приоритет операторов в Go и Java различается в отношении '-' и '››' ...
Мне нужно пойти другим путем, но, надеюсь, это будет проще без правильных сдвигов ... знаменитые последние слова!
Код Java:
https://stackoverflow.com/a/6162687/345165
// returns all higher 16 bits as 0 for all results
public static int fromFloat( float fval )
{
int fbits = Float.floatToIntBits( fval );
int sign = fbits >>> 16 & 0x8000; // sign only
int val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value
if( val >= 0x47800000 ) // might be or become NaN/Inf
{ // avoid Inf due to rounding
if( ( fbits & 0x7fffffff ) >= 0x47800000 )
{ // is or must become NaN/Inf
if( val < 0x7f800000 ) // was value but too large
return sign | 0x7c00; // make it +/-Inf
return sign | 0x7c00 | // remains +/-Inf or NaN
( fbits & 0x007fffff ) >>> 13; // keep NaN (and Inf) bits
}
return sign | 0x7bff; // unrounded not quite Inf
}
if( val >= 0x38800000 ) // remains normalized value
return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
if( val < 0x33000000 ) // too small for subnormal
return sign; // becomes +/-0
val = ( fbits & 0x7fffffff ) >>> 23; // tmp exp for subnormal calc
return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit
+ ( 0x800000 >>> val - 102 ) // round depending on cut off
>>> 126 - val ); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
}
Мой частичный порт:
func float32toUint16(f float32) uint16 {
fbits := math.Float32bits(f)
sign := uint16((fbits >> 16) & 0x00008000)
rv := (fbits & 0x7fffffff) + 0x1000
if rv >= 0x47800000 {
if (fbits & 0x7fffffff) >= 0x47800000 {
if rv < 0x7f800000 {
return sign | 0x7c00
}
return sign | 0x7c00 | uint16((fbits&0x007fffff)>>13)
}
return sign | 0x7bff
}
if rv >= 0x38800000 {
return sign | uint16((rv-0x38000000)>>13)
}
if rv < 0x33000000 {
return sign
}
rv = (fbits & 0x7fffffff) >> 23
return sign | uint16(((fbits&0x7fffff)|0x800000)+(0x800000>>(rv-102))>>(126-rv)) //these two shifts are my problem
}
func pack16(f1 float32, f2 float32) uint32 {
ui161 := float32toUint16(f1)
ui162 := float32toUint16(f2)
return ((uint32(ui161) << 16) | uint32(ui162))
}
Я нашел то, что выглядело как еще более эффективный код, без ветвления, но понимание механизма того, как это работает, чтобы иметь возможность его портировать, немного выходит за рамки моих ржавых (не языковых) навыков.
https://stackoverflow.com/a/5587983
Ваше здоровье
[Edit] Похоже, что код работает со значениями, которые я использую в настоящее время (это трудно быть точным, поскольку у меня нет опыта отладки шейдеров). Думаю, мой вопрос касается правильности моего переноса, особенно последних двух смен.
[Edit2] В свете дня я вижу, что я уже ошибся с приоритетом в одном месте и исправил приведенный выше пример.
измененный:
return sign | uint16(rv-(0x38000000>>13))
to:
return sign | uint16((rv-0x38000000)>>13)
float16
реализация: github.com/x448/float16, а вот ее _ 2_ реализация. - person icza   schedule 17.09.2020