c: 无需转换即可更改变量类型
c: change variable type without casting
我正在将 uint32_t 更改为浮点数,但没有更改实际位。
只是为了确定:我不想施放它。所以 float f = (float) i
与我不想做的完全相反,因为它改变了位。
我将使用它来将我的(伪)随机数转换为浮点数,而无需进行不必要的数学运算。
我目前正在做的和已经在做的是:
float random_float( uint64_t seed ) {
// Generate random and change bit format to ieee
uint32_t asInt = (random_int( seed ) & 0x7FFFFF) | (0x7E000000>>1);
// Make it a float
return *(float*)(void*)&asInt; // <-- pretty ugly and nees a variable
}
问题:现在我想摆脱asInt
变量,我想知道是否有更好/不那么丑的获取此变量的地址,转换两次并再次取消引用的方法?
您可以尝试并集 - 只要您确保类型在内存大小上相同:
union convertor {
int asInt;
float asFloat;
};
然后您可以将您的 int 分配给 asFloat(如果您愿意,也可以反过来)。当我一方面需要进行按位运算,另一方面仍然需要 uint32_t 表示数字时,我经常使用它
[编辑]
正如许多评论员所说的那样,您必须考虑不能用整数表示的值,例如 NAN、+INF、-INF、+0、-0。
将 int 的二进制表示重新解释为 float 会导致重大问题:
- 浮点数的二进制表示中有很多未定义的代码。
- 其他代码表示特殊条件,如 NAN、+INF、-INF、+0、-0(原文如此!)等
此外,如果这是一个随机值,即使捕获所有非值表示,也会产生非常糟糕的随机分布。
如果您在没有 FPU 的 MCU 上工作,您最好考虑完全避免浮动。一种替代方法可能是分数或缩放整数。有许多使用浮点数的算法实现,但可以很容易地转换为定点类型,精度损失可以接受(甚至 none)。有些甚至可能产生比 float 更高的精度(请注意,单精度 float 只有 23 位尾数,int32 将有 31 位(+ 1 符号),对于小数或固定缩放 int 相同。
请注意,C11 添加了(可选)对 _Frac 的支持。你可能想研究一下。
编辑:
根据您的评论,您似乎将 int 转换为 0..<1 范围内的浮点数。为此,您可以 assemble 对 uint32_t (例如原始值)使用位运算来浮点数。您只需要遵循 IEEE 格式(假设您的工具链确实符合 C 标准!参见 wikipedia。
结果(仍然是 uint32_t)然后可以由其他人已经描述的联合或指针重新解释。将其打包到一个系统相关的、注释良好的库中并深入挖掘。不要忘记检查字节顺序和对齐方式(对于 float 和 uint32_t 可能都是相同的,但对于位操作很重要)。
所以从您的代码来看,您似乎想要生成介于 0.5 和 1.0 之间的浮点数。
假设您的微控制器有一个支持浮点的标准 C 库,您可以在不涉及任何浮点运算的情况下按照所有标准执行此操作,您所需要的只是 ldexp
函数本身不实际上做任何浮点数学。
这看起来像这样:
return ldexpf((1 << 23) + random_thing_smaller_than_23_bits(), -24);
这里的诀窍是我们碰巧知道 IEEE754 二进制 32 浮点数的整数精度在 2^23 和 2^24 之间(我在这里可能会偏离一个,请仔细检查,我正在翻译这来自我在双打上所做的一些工作)。所以编译器应该知道如何将该数字简单地转换为浮点数。然后 ldexp
通过更改指数中的位将该数字乘以 2^-24。不涉及实际的浮点运算,也没有未定义的行为,代码完全可移植到任何具有 IEEE754 数字的标准 C 实现。仔细检查生成的代码,但是一个好的编译器和 c 库不应该在这里使用任何浮点指令。
如果您想查看我围绕生成随机浮点数所做的一些实验 you can peek at this github repo。这都是关于双打的,但应该可以简单地翻译成浮点数。
我正在将 uint32_t 更改为浮点数,但没有更改实际位。
只是为了确定:我不想施放它。所以 float f = (float) i
与我不想做的完全相反,因为它改变了位。
我将使用它来将我的(伪)随机数转换为浮点数,而无需进行不必要的数学运算。
我目前正在做的和已经在做的是:
float random_float( uint64_t seed ) {
// Generate random and change bit format to ieee
uint32_t asInt = (random_int( seed ) & 0x7FFFFF) | (0x7E000000>>1);
// Make it a float
return *(float*)(void*)&asInt; // <-- pretty ugly and nees a variable
}
问题:现在我想摆脱asInt
变量,我想知道是否有更好/不那么丑的获取此变量的地址,转换两次并再次取消引用的方法?
您可以尝试并集 - 只要您确保类型在内存大小上相同:
union convertor {
int asInt;
float asFloat;
};
然后您可以将您的 int 分配给 asFloat(如果您愿意,也可以反过来)。当我一方面需要进行按位运算,另一方面仍然需要 uint32_t 表示数字时,我经常使用它
[编辑]
正如许多评论员所说的那样,您必须考虑不能用整数表示的值,例如 NAN、+INF、-INF、+0、-0。
将 int 的二进制表示重新解释为 float 会导致重大问题:
- 浮点数的二进制表示中有很多未定义的代码。
- 其他代码表示特殊条件,如 NAN、+INF、-INF、+0、-0(原文如此!)等
此外,如果这是一个随机值,即使捕获所有非值表示,也会产生非常糟糕的随机分布。
如果您在没有 FPU 的 MCU 上工作,您最好考虑完全避免浮动。一种替代方法可能是分数或缩放整数。有许多使用浮点数的算法实现,但可以很容易地转换为定点类型,精度损失可以接受(甚至 none)。有些甚至可能产生比 float 更高的精度(请注意,单精度 float 只有 23 位尾数,int32 将有 31 位(+ 1 符号),对于小数或固定缩放 int 相同。
请注意,C11 添加了(可选)对 _Frac 的支持。你可能想研究一下。
编辑:
根据您的评论,您似乎将 int 转换为 0..<1 范围内的浮点数。为此,您可以 assemble 对 uint32_t (例如原始值)使用位运算来浮点数。您只需要遵循 IEEE 格式(假设您的工具链确实符合 C 标准!参见 wikipedia。
结果(仍然是 uint32_t)然后可以由其他人已经描述的联合或指针重新解释。将其打包到一个系统相关的、注释良好的库中并深入挖掘。不要忘记检查字节顺序和对齐方式(对于 float 和 uint32_t 可能都是相同的,但对于位操作很重要)。
所以从您的代码来看,您似乎想要生成介于 0.5 和 1.0 之间的浮点数。
假设您的微控制器有一个支持浮点的标准 C 库,您可以在不涉及任何浮点运算的情况下按照所有标准执行此操作,您所需要的只是 ldexp
函数本身不实际上做任何浮点数学。
这看起来像这样:
return ldexpf((1 << 23) + random_thing_smaller_than_23_bits(), -24);
这里的诀窍是我们碰巧知道 IEEE754 二进制 32 浮点数的整数精度在 2^23 和 2^24 之间(我在这里可能会偏离一个,请仔细检查,我正在翻译这来自我在双打上所做的一些工作)。所以编译器应该知道如何将该数字简单地转换为浮点数。然后 ldexp
通过更改指数中的位将该数字乘以 2^-24。不涉及实际的浮点运算,也没有未定义的行为,代码完全可移植到任何具有 IEEE754 数字的标准 C 实现。仔细检查生成的代码,但是一个好的编译器和 c 库不应该在这里使用任何浮点指令。
如果您想查看我围绕生成随机浮点数所做的一些实验 you can peek at this github repo。这都是关于双打的,但应该可以简单地翻译成浮点数。