在这里,我们将给大家分享关于C#:将ushort转换为float的知识,让您更了解c#float转uint的本质,同时也会涉及到如何更有效地assembly–使用Bitwise将Int转换为Float
在这里,我们将给大家分享关于C#:将ushort转换为float的知识,让您更了解c#float转uint的本质,同时也会涉及到如何更有效地assembly – 使用Bitwise将Int转换为Float/Float到Int、assembly – 将uint32的向量转换为float向量的最有效方法?、CoreML自动将int转换为float、C#Float表达式:将结果float转换为int时的奇怪行为的内容。
本文目录一览:- C#:将ushort转换为float(c#float转uint)
- assembly – 使用Bitwise将Int转换为Float/Float到Int
- assembly – 将uint32的向量转换为float向量的最有效方法?
- CoreML自动将int转换为float
- C#Float表达式:将结果float转换为int时的奇怪行为
C#:将ushort转换为float(c#float转uint)
我想在float数组中转换它们:第一个ushort表示第一个float的16个MSB,第二个ushort是第一个float的16个LSB,依此类推.
我尝试使用类似下面的内容,但是值被转换为整数的值,而不是原始位:
ushort[] buffer = { 0xBF80,0x0000 }; float f = (uint)buffer[0] << 16 | buffer[1]; // expected result => f == -1 (0xBF800000) // effective result => f == 3.21283686E+9 (0x4F3F8000)
有什么建议吗?
解决方法
特别是,ToSingle方法采用一系列字节并将它们转换为浮点数.
ushort[] buffer = {0xBF80,0x0000}; byte[] bytes = new byte[4]; bytes[0] = (byte)(buffer[1] & 0xFF); bytes[1] = (byte)(buffer[1] >> 8); bytes[2] = (byte)(buffer[0] & 0xFF); bytes[3] = (byte)(buffer[0] >> 8); float value = BitConverter.ToSingle( bytes,0 );
编辑在这个例子中,我已经颠倒了MSB / LSB顺序..现在它是正确的
assembly – 使用Bitwise将Int转换为Float/Float到Int
根据我目前所知,对于int to float,你必须将整数转换为二进制,通过查找有效数,指数和分数来规范化整数的值,然后从那里输出float中的值?
至于float到int,你必须将值分成有效数,指数和分数,然后反转上面的指令得到一个int值?
我试着按照这个问题的说明:Casting float to int (bitwise) in C
但我真的无法理解它.
另外,有人可以解释为什么在将int转换为float时大于23位的值需要舍入?
提前致谢
解决方法
现在来一些肉.
下面的代码是简单的,并且试图从0的范围内的unsigned int产生IEEE-754单精度浮点数.价值< 224.这是您最有可能在现代硬件上遇到的格式,它是您在原始问题中引用的格式. IEEE-754单精度浮点数分为三个字段:单符号位,8位指数和23位有效数(有时称为尾数). IEEE-754使用隐藏的1有效数,意味着有效数实际上是24位.这些位从左到右打包,符号位在第31位,指数在第30位…… 23位,有效位在位22 .. 0中.来自维基百科的下图说明: 指数的偏差为127,这意味着与浮点数关联的实际指数比指数字段中存储的值小127.因此,指数0将被编码为127. (注意:完整的维基百科文章可能对您有意思.参考:http://en.wikipedia.org/wiki/Single_precision_floating-point_format)
因此,IEEE-754号码0x40000000解释如下:
>位31 = 0:正值
>位30 .. 23 = 0x80:指数= 128 – 127 = 1(又称21)
>位22 .. 0均为0:有效值= 1.00000000_00000000_0000000. (注意我恢复了隐藏的1).
所以值是1.0 x 21 = 2.0.
要在上面给出的有限范围内转换unsigned int,然后转换为IEEE-754格式的东西,您可以使用类似下面的函数.它采取以下步骤:
>将整数的前导1与浮点表示中隐藏1的位置对齐.
>在对齐整数时,记录所做的轮班总数.
>掩盖隐藏的1.
>使用所做的班次数,计算指数并将其附加到数字上.
>使用reinterpret_cast,将生成的位模式转换为浮点数.这部分是一个丑陋的黑客,因为它使用类型惩罚指针.你也可以通过滥用工会来做到这一点.某些平台提供内在操作(例如_itof)以使这种重新解释不那么难看.
有更快的方法可以做到这一点;如果不是超级有效的话,这个是教学上有用的:
float uint_to_float(unsigned int significand) { // Only support 0 < significand < 1 << 24. if (significand == 0 || significand >= 1 << 24) return -1.0; // or abort(); or whatever you'd like here. int shifts = 0; // Align the leading 1 of the significand to the hidden-1 // position. Count the number of shifts required. while ((significand & (1 << 23)) == 0) { significand <<= 1; shifts++; } // The number 1.0 has an exponent of 0,and would need to be // shifted left 23 times. The number 2.0,however,has an // exponent of 1 and needs to be shifted left only 22 times. // Therefore,the exponent should be (23 - shifts). IEEE-754 // format requires a bias of 127,though,so the exponent field // is given by the following expression: unsigned int exponent = 127 + 23 - shifts; // Now merge significand and exponent. Be sure to strip away // the hidden 1 in the significand. unsigned int merged = (exponent << 23) | (significand & 0x7FFFFF); // Reinterpret as a float and return. This is an evil hack. return *reinterpret_cast< float* >( &merged ); }
您可以使用检测数字中前导1的函数来提高此过程的效率. (这些有时会出现像clz这样的名称,用于“计数前导零”,或者用于“标准化”的标准.)
您还可以通过记录符号,取整数的绝对值,执行上述步骤,然后将符号放入数字的第31位,将其扩展为有符号数.
对于整数> = 224,整个整数不适合32位浮点格式的有效数字段.这就是你需要“舍入”的原因:你失去了LSB以使价值合适.因此,多个整数将最终映射到相同的浮点模式.确切的映射取决于舍入模式(向-Inf舍入,向Inf舍入,向零舍入,向最近偶数舍入).但事实是你不能将24位推到少于24位而没有一些损失.
您可以根据上面的代码看到这一点.它通过将前导1对齐到隐藏的1位置来工作.如果值>> 224,则代码需要向右移动而不是向左移动,这必然会使LSB移位.舍入模式只是告诉您如何处理移位的位.
assembly – 将uint32的向量转换为float向量的最有效方法?
编辑:
为了澄清,我想做以下标量操作的向量序列:
unsigned int x = ... float res = (float)x;
EDIT2:这是一个用于进行标量转换的简单算法.
unsigned int x = ... float bias = 0.f; if (x > 0x7fffffff) { bias = (float)0x80000000; x -= 0x80000000; } res = signed_convert(x) + bias;
解决方法
在我的头顶,你可以做如下正确的转换(正如我所说,这是我的头顶,所以它可能在某处保存指令):
movdqa xmm1,xmm0 // make a copy of x psrld xmm0,16 // high 16 bits of x pand xmm1,[mask] // low 16 bits of x orps xmm0,[onep39] // float(2^39 + high 16 bits of x) cvtdq2ps xmm1,xmm1 // float(low 16 bits of x) subps xmm0,[onep39] // float(high 16 bits of x) addps xmm0,xmm1 // float(x)
常量具有以下值:
mask: 0000ffff 0000ffff 0000ffff 0000ffff onep39: 53000000 53000000 53000000 53000000
这样做是将每个通道的高半部分和低半部分分别转换为浮点数,然后将这些转换后的值相加.因为每一半只有16位宽,所以转换为float不会产生任何舍入.仅在添加两半时才进行舍入;因为加法是一个正确舍入的操作,所以整个转换都是正确舍入的.
相比之下,你的天真实现首先将低31位转换为浮点数,这会导致舍入,然后有条件地将2 ^ 31加到该结果,这可能会导致第二次舍入.每当你在转换中有两个单独的舍入点时,除非你非常小心它们是如何发生的,否则你不应该期望结果被正确舍入.
CoreML自动将int转换为float
您可以使用coremltools Python库将模型输出的数据类型更改为INT32。这样会将这些数字从浮点数自动转换为整数。
类似这样的东西:
import coremltools
model = coremltools.models.MLModel("YourModel.mlmodel")
spec = model._spec
spec.description.output[0].type.multiArrayType.dataType = coremltools.proto.FeatureTypes_pb2.ArrayFeatureType.INT32
coremltools.models.utils.save_spec(spec,"YourNewModel.mlmodel")
之所以不能自动完成,是因为通常浮点数是正确的数据类型。
C#Float表达式:将结果float转换为int时的奇怪行为
我有以下简单代码:
int speed1 = (int)(6.2f * 10);float tmp = 6.2f * 10;int speed2 = (int)tmp;
speed1
并且speed2
应该具有相同的值,但实际上,我有:
speed1 = 61speed2 = 62
我知道我可能应该使用Math.Round而不是强制转换,但是我想了解为什么值不同。
我查看了生成的字节码,但是除了存储和加载之外,操作码是相同的。
我也在Java中尝试了相同的代码,并且正确获得了62和62。
有人可以解释吗?
编辑: 在实际代码中,它不是直接6.2f * 10,而是函数调用*常量。我有以下字节码:
为speed1
:
IL_01b3: ldloc.s V_8IL_01b5: callvirt instance float32 myPackage.MyClass::getSpeed()IL_01ba: ldc.r4 10.IL_01bf: mulIL_01c0: conv.i4IL_01c1: stloc.s V_9
为speed2
:
IL_01c3: ldloc.s V_8IL_01c5: callvirt instance float32 myPackage.MyClass::getSpeed()IL_01ca: ldc.r4 10.IL_01cf: mulIL_01d0: stloc.s V_10IL_01d2: ldloc.s V_10IL_01d4: conv.i4IL_01d5: stloc.s V_11
我们可以看到操作数是浮点数,唯一的区别是stloc/ldloc
。
至于虚拟机,我尝试使用Mono / Win7,Mono / MacOS和.NET / Windows,结果相同。
答案1
小编典典首先,我假设您知道6.2f *10
由于浮点舍入而不是62(实际上是61.99999809265137的值,用a表示double
),并且您的问题只是关于为什么两个看似相同的计算导致错误的值。
答案是,对于(int)(6.2f * 10)
,您将double
取值61.99999809265137并将其截断为整数,得到61。
对于float f = 6.2f * 10
,您将采用双精度值61.99999809265137并 四舍五入
到最接近的float
值62,然后将其截断float
为整数,结果为62。
练习:解释以下操作序列的结果。
double d = 6.2f * 10;int tmp2 = (int)d;// evaluate tmp2
更新:由于在评论所指出的,表达6.2f *10
是一个正式的float
,因为第二参数具有一个隐式转换到float
其更好比隐式转换double
。
实际的问题是允许(但不是必需)编译器使用比正式类型(第11.2.2节)更高精度的中间件。这就是为什么您在不同的系统上看到不同的行为的原因:在表达式中(int)(6.2f *10)
,编译器可以选择6.2f * 10
在转换为之前将值保持为高精度中间形式int
。如果是,则结果为61。如果不是,则结果为62。
在第二个示例中,显式赋值float
强制四舍五入发生在转换为整数之前。
我们今天的关于C#:将ushort转换为float和c#float转uint的分享已经告一段落,感谢您的关注,如果您想了解更多关于assembly – 使用Bitwise将Int转换为Float/Float到Int、assembly – 将uint32的向量转换为float向量的最有效方法?、CoreML自动将int转换为float、C#Float表达式:将结果float转换为int时的奇怪行为的相关信息,请在本站查询。
本文标签: