尝试了适用于任何大小 int 的解决方案。
不依赖2的恭维。
适用于 INT_MIN。
从@Floris 学到了很多东西
[编辑] 调整做舍入和其他改进
#include <stdio.h>
int Round(uint32_t Odd, unsigned RoundBit, unsigned StickyBit, uint32_t Result);
int Inexact;
// Select your signed integer type: works with any one
//typedef int8_t integer;
//typedef int16_t integer;
//typedef int32_t integer;
typedef int64_t integer;
//typedef intmax_t integer;
uint32_t int_to_IEEEfloat(integer x) {
uint32_t Result;
if (x < 0) { // Note 1
Result = 0x80000000;
} else {
Result = 0;
x = -x; // Use negative absolute value. Note 2
}
if (x) {
uint32_t Expo = 127 + 24 - 1;
static const int32_t m2Power23 = -0x00800000;
static const int32_t m2Power24 = -0x01000000;
unsigned RoundBit = 0;
unsigned StickyBit = 0;
while (x <= m2Power24) { // Note 3
StickyBit |= RoundBit;
RoundBit = x&1;
x /= 2;
Expo++;
}
// Round. Note 4
if (Round(x&1, RoundBit, StickyBit, Result) && (--x <= m2Power24)) {
x /= 2;
Expo++;
}
if (RoundBit | StickyBit) { // Note 5
Inexact = 1; // TBD: Set FP inexact flag
}
int32_t i32 = x; // Note 6
while (i32 > m2Power23) {
i32 *= 2;
Expo--;
}
if (Expo >= 0xFF) {
Result |= 0x7F800000; // Infinity Note 7
} else {
Result |= (Expo << 23) | ((-i32) & 0x007FFFFF);
}
}
return Result;
}
/*
Note 1 If `integer` was a signed-magnitude or 1s compliment, then +0 and -0 exist.
Rather than `x<0`, this should be a test if the sign bit is set.
The following `if (x)` will not be taken on +0 and -0.
This provides the corresponding float +0.0 and -0.0 be returned.
Note 2 Overflow will _not_ occur using 2s compliment, 1s compliment or sign magnitude.
We are insuring x at this point is < 0.
Note 3 Right shifting may shift out a 1. Use RoundBit and StickyBit to keep
track of bits shifted out for later rounding determination.
Note 4 Round as needed here. Possible to need to shift once more after rounding.
Note 5 If either RoundBit or StickyBit set, the floating point inexact flag may be set.
Note 6 Since the `Integer` type maybe be less than 32 bits, we need to convert
to a 32 bit integer as IEEE float is 32 bits.FILE
Note 7 Infinity only expected in Integer was 129 bits or larger.
*/
int Round(uint32_t Odd, unsigned RoundBit, unsigned StickyBit, uint32_t Result) {
// Round to nearest, ties to even
return (RoundBit) && (Odd || StickyBit);
// Truncate toward 0
// return 0;
// Truncate away from 0
// return RoundBit | StickyBit
// Truncate toward -Infinity
// return (RoundBit | StickyBit) || Result
}
// For testing
float int_to_IEEEfloatf(integer x) {
union {
float f;
uint32_t u;
} xx; // Overlay a float with a 32-bit unsigned integer
Inexact = 0;
printf("%20lld ", (long long) x);
xx.u = int_to_IEEEfloat(x);
printf("%08lX ", (long) xx.u);
printf("%d : ", Inexact);
printf("%.8e\n", xx.f);
return xx.f;
}
int main() {
int_to_IEEEfloatf(0x0);
int_to_IEEEfloatf(0x1);
int_to_IEEEfloatf(-0x1);
int_to_IEEEfloatf(127);
int_to_IEEEfloatf(-128);
int_to_IEEEfloatf(12345);
int_to_IEEEfloatf(32767);
int_to_IEEEfloatf(-32768);
int_to_IEEEfloatf(16777215);
int_to_IEEEfloatf(16777216);
int_to_IEEEfloatf(16777217);
int_to_IEEEfloatf(2147483647L);
int_to_IEEEfloatf(-2147483648L);
int_to_IEEEfloatf( 9223372036854775807LL);
int_to_IEEEfloatf(-9223372036854775808LL);
return 0;
}